Source: / # Tanomis documentation Run AI agents as a governed service. You write the agent; Tanomis packages it, runs it per tenant, and gives every action an identity, a policy check, and a record. Run AI agents as a governed service. You write the agent in Python. Tanomis packages it, runs it for each of your customers in its own sandbox, and gives every action an identity, a policy check, and a record. Warm starts, approvals, and audit come with the platform, not with your code. [Deploy your first agent](/getting-started/quickstart) about 10 minutes [Tour the Console](/getting-started/console-tour) ```bash title="Four commands, one running agent" tanomis login tanomis agent new support-copilot --harness claude-agents-sdk tanomis agent deploy ./agents/support-copilot --tenant northwind-sandbox tanomis session run support-copilot --prompt "Draft a reply to ticket 4821." ``` - Sandbox per session - Warm start under a second - Signed, immutable builds - Tamper-evident audit ledger - Your tenants, your policy ## How it works Your source becomes an immutable **version**. A tenant is **enabled** for it. Your application opens a **session** and starts **runs**. Every run leaves **evidence**. Nothing skips a step, and nothing routes traffic until you enable it. 1. **Source** Your Python agent, in your repository. 2. **Version** An immutable build plus the contract you declared. 3. **Enablement** Which version each tenant serves, canary or baseline. 4. **Session** A conversation or unit of work, warm-started in under a second. 5. **Run** One request, streamed, pausable for a person. 6. **Evidence** Who asked, what was allowed, what happened, signed. ## What teams build on it Three agents that use most of the platform. Each one starts from a guide you can follow today. Customer-facing ### Support copilot Reads the ticket and your knowledge base through a governed connector, drafts the reply, and pauses for a person before issuing a refund. - MCP connector - Approval on refund - Streaming [Build it](/getting-started/quickstart) Back office ### Invoice reconciler Picks up a unit of durable work, matches it against the ledger over several sessions, and settles it once. No person ends it by hand. - Durable work - Session files - Egress allowlist [Build it](/guides/run/durable-work-and-settlement) Engineering ### Code review assistant Reads a pull request through the GitHub connector, hands the security pass to a second agent under a narrower grant, and posts one review. - Delegation - Schema pins - Canary rollout [Build it](/guides/run/delegate-to-another-agent) ## Everything the platform gives you The feature set, in the order you meet it. **Build** your code, your model [Harnesses and models](/guides/build/choose-a-harness) Claude Agent SDK or Deep Agents; any model on the tenant's allowlist, routed through one gateway. [Governed tools](/guides/connect-tools/mcp-connectors) MCP connectors with an explicit allowlist. Agents never launch servers themselves. [One manifest](/guides/build/manifest-essentials) Models, tools, hosts, limits, and approvals declared once in agent.yaml. **Ship** immutable, per tenant [Versions and canary](/guides/deploy/rollout-and-rollback) One command builds, registers, snapshots. Roll out by tenant share; roll back by re-enabling. [Warm starts](/concepts/agents-and-versions) A golden snapshot restores the harness in under a second, with no tenant data inside. [CI and automation](/guides/deploy/deploy-from-ci) Dry-run on every pull request; deploy with an organization-owned credential. **Run** from your product [Sessions and streaming](/guides/run/invoke-from-your-application) HTTP in, events out. Reconnect and replay safely; attach files with integrity checks. [Approvals and questions](/guides/run/approval-required-actions) The run pauses, survives disconnects, and resumes on the first answer, from the Console or your app. [Delegation](/guides/run/delegate-to-another-agent) One agent hands work to another under a live grant that only narrows. **Trust** what auditors ask for [Policy ceilings](/guides/govern/restrict-network-egress) Network egress, model, and tool ceilings per tenant; the manifest can only ask for less. [Evaluation](/guides/evaluate/overview) Trace every run, score a candidate against the baseline, gate promotion on the verdict. [Audit ledger](/guides/audit/audit-and-evidence) Append-only, hash-chained, signed per tenant. Export an auditor package in one step. ## Every run is governed Whatever the agent does, the same six things happen around it. The example below follows one run end to end: a support copilot answering a ticket for the customer bluewater-bank, that needed a refund approved before it could finish. Read the whole model in [Policy, approvals, and evidence](/concepts/governance). ## Start where you are Building an agent [Quickstart](/getting-started/quickstart) Then [Manifest essentials](/guides/build/manifest-essentials) and [MCP connectors](/guides/connect-tools/mcp-connectors). Integrating an application [Invoke from your application](/guides/run/invoke-from-your-application) Then [Sessions and runs](/concepts/sessions-and-runs) and the [Platform API](/api). Administering a workspace [Tour the Console](/getting-started/console-tour) Then [Members and access](/guides/manage/organization-members) and [Audit and evidence](/guides/audit/audit-and-evidence). Working with a coding agent such as Claude Code, Codex, or Cursor? One command installs skills that teach it to operate Tanomis and registers a documentation MCP server: see [Build with a coding agent](/getting-started/coding-agents). ## Reference [ **CLI** Every tanomis command and flag.](/cli) [ **agent.yaml** Manifest fields and defaults.](/reference/agent-manifest) [ **Platform API** Endpoints, headers, errors.](/api) [ **Events** Run event types and replay.](/guides/run/stream-and-replay-events) --- Source: /api # Platform API The application-facing HTTP surface for agents that are already deployed and enabled for the tenant selected by your workspace. The application-facing HTTP surface for agents that are already deployed and enabled for the tenant selected by your workspace. Use it when your product or backend needs to invoke an agent directly. Routes used by the Tanomis control plane, an agent sandbox, or a platform operator are not part of this reference. For machine authentication, see [Authenticate a backend](/guides/run/authenticate-your-backend). Human owner/admin operations for credentials and tenants are documented in the [Organization management API](/reference/organization-management-api). Start with [Sessions and runs](/concepts/sessions-and-runs) when choosing an invocation boundary or designing retry and recovery behavior; return here for exact requests and responses. Use `tanomis` to author, deploy, and roll out agents. Generated from the Platform API specification on 2026-09-04. ## Request basics The hosted API origin is: ```text https://api.tanomis.com ``` Keep the origin free of a trailing `/v1`; every endpoint includes its own version prefix. For another Tanomis environment, use the API origin provided by its administrator. ### Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | > **Authentication:** a backend authenticates with an organization-owned [automation client](/guides/deploy/automation-credentials): exchange its client ID and secret for a short-lived token as described in [Authenticate a backend](/guides/run/authenticate-your-backend). The CLI's human login is not an application credential, and there is no separate token endpoint to guess from these examples. Selecting a workspace projection supplies request context; the workspace does not own the tenant or its resources. A tenant header cannot widen a customer token. Do not use it to try to switch to a tenant the token does not grant. ### Your first call With a token in hand, three requests take you from nothing to a running agent: ```text title="1. Discover the workspace" GET /v1/me/workspaces Authorization: Bearer ``` ```text title="2. Open a session" POST /v1/sessions Authorization: Bearer X-Organization-ID: X-Workspace-Tenant-ID: { "agent_name": "support-copilot" } ``` ```text title="3. Start a run and follow it" POST /v1/sessions/{session_id}/runs { "input": { "prompt": "Draft a reply to ticket 4821." }, "idempotency_key": "ticket-4821-1" } GET /v1/sessions/{session_id}/runs/{run_id}/events ``` The walkthrough with responses is [Invoke from your application](/guides/run/invoke-from-your-application). ### Data conventions - Request and response bodies are JSON unless an endpoint says otherwise. - Unknown request fields are rejected instead of ignored. - Resource IDs are opaque strings. Store them exactly as returned. - Times use RFC 3339 UTC strings, such as `2026-09-02T11:42:18Z`. - Optional response fields may be absent. Clients should ignore response fields they do not know. ## Endpoints ### Workspaces | Method | Path | Operation | |---|---|---| | `GET` | `/v1/whoami` | [Inspect the caller](/api/workspaces/whoami) | | `GET` | `/v1/me/workspaces` | [List workspaces](/api/workspaces/list-workspaces) | ### Agents | Method | Path | Operation | |---|---|---| | `GET` | `/v1/agents` | [List agents](/api/agents/list-agents) | | `POST` | `/v1/agents/{agent_name}/runs` | [Invoke once and stream](/api/agents/invoke-agent) | ### Sessions | Method | Path | Operation | |---|---|---| | `POST` | `/v1/sessions` | [Create a session](/api/sessions/create-session) | | `GET` | `/v1/sessions` | [List sessions](/api/sessions/list-sessions) | | `GET` | `/v1/sessions/{session_id}` | [Read a session](/api/sessions/get-session) | | `POST` | `/v1/sessions/{session_id}/stop` | [Stop a session](/api/sessions/stop-session) | | `POST` | `/v1/sessions/{session_id}/resume` | [Resume a session](/api/sessions/resume-session) | ### Runs | Method | Path | Operation | |---|---|---| | `POST` | `/v1/sessions/{session_id}/runs` | [Start a run](/api/runs/start-run) | | `GET` | `/v1/sessions/{session_id}/runs` | [List runs](/api/runs/list-runs) | | `GET` | `/v1/sessions/{session_id}/runs/{run_id}` | [Read a run](/api/runs/get-run) | | `GET` | `/v1/sessions/{session_id}/runs/{run_id}/events` | [Stream run events](/api/runs/stream-run-events) | | `POST` | `/v1/sessions/{session_id}/runs/{run_id}/cancel` | [Cancel a run](/api/runs/cancel-run) | ### Interactions | Method | Path | Operation | |---|---|---| | `GET` | `/v1/sessions/{session_id}/interactions` | [List interactions](/api/interactions/list-interactions) | | `POST` | `/v1/sessions/{session_id}/interactions/{interaction_key}/answer` | [Answer an interaction](/api/interactions/answer-interaction) | ### Files | Method | Path | Operation | |---|---|---| | `POST` | `/v1/sessions/{session_id}/files` | [Prepare a file](/api/files/prepare-file) | | `GET` | `/v1/sessions/{session_id}/files` | [List files](/api/files/list-files) | | `PUT` | `/v1/sessions/{session_id}/files/{file_id}/content` | [Upload file bytes](/api/files/upload-file-content) | | `GET` | `/v1/sessions/{session_id}/files/{file_id}/content` | [Download file bytes](/api/files/download-file-content) | | `DELETE` | `/v1/sessions/{session_id}/files/{file_id}` | [Delete a file](/api/files/delete-file) | > **Availability:** Session-file endpoints appear only when file attachments are enabled for the environment. Read `file_attachments_enabled` from `GET /v1/whoami`, then `session_files_supported` on the session before showing those actions. ## Complete request flow This managed-workspace example creates a reusable session, starts one run, and follows its events. It requires `curl` and `jq`. Copy the organization and workspace selectors from `GET /v1/me/workspaces`: ```bash title="Terminal" export TANOMIS_API="https://api.tanomis.com" export TANOMIS_TOKEN="paste-a-short-lived-access-token" export TANOMIS_ORG="org_abc123" export TANOMIS_WORKSPACE="customer-production" SESSION_ID="$( curl --silent --show-error --fail-with-body \\ --request POST \\ "$TANOMIS_API/v1/sessions" \\ --header "Authorization: Bearer $TANOMIS_TOKEN" \\ --header "X-Organization-ID: $TANOMIS_ORG" \\ --header "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" \\ --header "Content-Type: application/json" \\ --data '{"agent_name":"support-copilot","idempotency_key":"api-example-session-1"}' \\ | jq --raw-output '.id' )" RUN_ID="$( curl --silent --show-error --fail-with-body \\ --request POST \\ "$TANOMIS_API/v1/sessions/$SESSION_ID/runs" \\ --header "Authorization: Bearer $TANOMIS_TOKEN" \\ --header "X-Organization-ID: $TANOMIS_ORG" \\ --header "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" \\ --header "Content-Type: application/json" \\ --data '{"input":{"prompt":"Summarize the open customer issue."},"idempotency_key":"api-example-run-1"}' \\ | jq --raw-output '.id' )" curl --no-buffer --fail-with-body \\ "$TANOMIS_API/v1/sessions/$SESSION_ID/runs/$RUN_ID/events" \\ --header "Authorization: Bearer $TANOMIS_TOKEN" \\ --header "X-Organization-ID: $TANOMIS_ORG" \\ --header "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" \\ --header "Accept: text/event-stream" ``` When no more work will use the session, stop it with [Stop a session](/api/sessions/stop-session). ## Errors and retries JSON errors include a machine-readable `error` code. Most application errors also include a human-readable `message`; clients must tolerate a code-only response from the access gate: ```json { "error": "invalid_status", "message": "unknown interaction status" } ``` | Status | Meaning | Client action | |---|---|---| | `400` | Invalid JSON, fields, selectors, or query parameters. | Fix the request; do not retry unchanged. | | `401` | Missing, expired, or invalid access token. | Obtain a new token. | | `403` | The caller does not have access to the selected organization/tenant projection or operation. | Change the grant or operation; do not retry unchanged. | | `404` | The resource is absent in the selected tenant, or an optional capability is unavailable. | Check the selected workspace projection and resource id. | | `409` | Lifecycle or idempotency conflict. | Read the returned or current resource before deciding whether to retry. | | `412` | An idempotent create refers to a session that is already terminal. | Create a replacement with a new key if that is the intended action. | | `429` | Request rate exceeded. | Retry with exponential backoff and jitter. | | `500–599` | Temporary platform or upstream failure. | Retry boundedly with backoff. | Only retry a create or run request automatically when it carries the same `idempotency_key` and the same request body. For event streams, reconnect with `Last-Event-ID` instead of starting a new run. ## Support boundary The endpoints on this page are the supported application-invocation surface. Use the [CLI](/cli) for deployment and inspection and the [Agent manifest](/reference/agent-manifest) for version configuration. Routes used by the Tanomis control plane, an agent sandbox, or a platform operator are intentionally not part of this customer reference. --- Source: /api/agents/invoke-agent # Invoke once and stream POST /v1/agents/{agent_name}/runs: Create a session and stream one run in a single request. POST/v1/agents/{agent_name}/runs Create a session and stream one run in a single request. The `200 OK` server-sent event stream carries `X-Session-ID` and `X-Run-ID` response headers; store both before processing events so the resources stay inspectable after a disconnect. This shortcut gives a retrying client no stable session boundary: use an explicit session for retry-safe admission, multiple runs, file attachments, durable human interaction, or independent reconnection. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `agent_name` | string | An agent enabled for the selected tenant, from List agents. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | | `Accept` | yes | Send `text/event-stream`. | ## Request body | Field | Type | Required | Description | |---|---|---|---| | `input` | object | required | Passed to the agent as JSON. The shape is defined by your agent's entrypoint. | ## Responses | Status | Meaning | |---|---| | `200` | A server-sent event stream of the run's events; see Stream run events for the frame format and terminal events. | ## Request ```bash curl -X POST "https://api.tanomis.com/v1/agents/support-copilot/runs" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" \ -H "Accept: text/event-stream" \ -H "Content-Type: application/json" \ -d '{ "input": { "prompt": "Summarize the open customer issue." } }' ``` ```python import httpx response = httpx.post( f"https://api.tanomis.com/v1/agents/{agent_name}/runs", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, "Accept": "text/event-stream", }, json={ "input": { "prompt": "Summarize the open customer issue.", }, }, ) for line in response.iter_lines(): print(line) ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/agents/${agentName}/runs`, { method: "POST", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, "Accept": "text/event-stream", "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Summarize the open customer issue." } }), }); const reader = response.body.getReader(); // Decode server-sent events from the stream; see Stream and replay events. ``` --- Source: /api/agents/list-agents # List agents GET /v1/agents: Return the agents enabled for the selected tenant. GET/v1/agents Return the agents enabled for the selected tenant. Use `name` as `agent_name` when creating a session or in the one-shot run URL. The returned version id is informational and is not an invocation selector. Generated from the Platform API specification on 2026-09-04. ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Responses | Status | Meaning | |---|---| | `200` | Agents enabled for the tenant. | ## Request ```bash curl -X GET "https://api.tanomis.com/v1/agents" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` ```python import httpx response = httpx.get( f"https://api.tanomis.com/v1/agents", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/agents`, { method: "GET", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, }, }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="200 OK" { "agents": [ { "name": "support-copilot", "scope": "global", "version": "0.4.0", "version_id": "ver_a82d1f", "status": "production", "framework": "claude-agents-sdk" } ] } ``` --- Source: /api/files/delete-file # Delete a file DELETE /v1/sessions/{session_id}/files/{file_id}: Delete a file from the session. DELETE/v1/sessions/{session_id}/files/{file_id} Delete a file from the session. Available even when the agent version lacks file support. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `session_id` | string | The session, as returned by Create a session. | | `file_id` | string | The file, as returned by Prepare a file. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Responses | Status | Meaning | |---|---| | `204` | The file was deleted. | | `404` | No such file, or attachments are disabled. | ## Request ```bash curl -X DELETE "https://api.tanomis.com/v1/sessions/ses_7d4f9c/files/file_b172c9" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` ```python import httpx response = httpx.delete( f"https://api.tanomis.com/v1/sessions/{session_id}/files/{file_id}", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, ) for line in response.iter_lines(): print(line) ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions/${sessionId}/files/${fileId}`, { method: "DELETE", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, }, }); const reader = response.body.getReader(); // Decode server-sent events from the stream; see Stream and replay events. ``` --- Source: /api/files/download-file-content # Download file bytes GET /v1/sessions/{session_id}/files/{file_id}/content: Download the file's bytes. GET/v1/sessions/{session_id}/files/{file_id}/content Download the file's bytes. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `session_id` | string | The session, as returned by Create a session. | | `file_id` | string | The file, as returned by Prepare a file. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Responses | Status | Meaning | |---|---| | `200` | The bytes, with the file's media type. | | `404` | No such file, or attachments are disabled. | ## Request ```bash curl -X GET "https://api.tanomis.com/v1/sessions/ses_7d4f9c/files/file_b172c9/content" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` ```python import httpx response = httpx.get( f"https://api.tanomis.com/v1/sessions/{session_id}/files/{file_id}/content", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, ) for line in response.iter_lines(): print(line) ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions/${sessionId}/files/${fileId}/content`, { method: "GET", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, }, }); const reader = response.body.getReader(); // Decode server-sent events from the stream; see Stream and replay events. ``` --- Source: /api/files/list-files # List files GET /v1/sessions/{session_id}/files: List the files in the session with their status. GET/v1/sessions/{session_id}/files List the files in the session with their status. Available even when the agent version lacks file support, so existing files can be inspected. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `session_id` | string | The session, as returned by Create a session. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Responses | Status | Meaning | |---|---| | `200` | The session's files. | ## Request ```bash curl -X GET "https://api.tanomis.com/v1/sessions/ses_7d4f9c/files" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` ```python import httpx response = httpx.get( f"https://api.tanomis.com/v1/sessions/{session_id}/files", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions/${sessionId}/files`, { method: "GET", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, }, }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="200 OK" { "files": [ { "id": "file_b172c9", "filename": "case-summary.pdf", "media_type": "application/pdf", "size_bytes": 48213, "status": "ready", "created_at": "2026-09-02T11:45:00Z" } ] } ``` --- Source: /api/files/prepare-file # Prepare a file POST /v1/sessions/{session_id}/files: Prepare file metadata before uploading bytes. POST/v1/sessions/{session_id}/files Prepare file metadata before uploading bytes. Check `session_files_supported` on the session first. The response carries an opaque `id` and a `content_url`; upload exactly `size_bytes` bytes there. File routes return `404` when attachments are disabled for the environment, and prepare and content transfer return `409` when this agent version lacks file support. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `session_id` | string | The session, as returned by Create a session. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Request body | Field | Type | Required | Description | |---|---|---|---| | `filename` | string | required | | | `media_type` | string | required | | | `size_bytes` | integer | required | | | `sha256` | string | required | Hex digest of the bytes you will upload. | | `idempotency_key` | string | optional | Replays the same prepared file for the same request. | ## Responses | Status | Meaning | |---|---| | `201` | The prepared file. | | `404` | File attachments are disabled for the environment, or no such session. | | `409` | The deployed agent version does not support files. | ## Request ```bash curl -X POST "https://api.tanomis.com/v1/sessions/ses_7d4f9c/files" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" \ -H "Content-Type: application/json" \ -d '{ "filename": "case-summary.pdf", "media_type": "application/pdf", "size_bytes": 48213, "sha256": "83e0ef2f426dfad68f9b420304fc19414f8451a6bf24667f7e61c50e9ca57f14", "idempotency_key": "case-summary-v1" }' ``` ```python import httpx response = httpx.post( f"https://api.tanomis.com/v1/sessions/{session_id}/files", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, json={ "filename": "case-summary.pdf", "media_type": "application/pdf", "size_bytes": 48213, "sha256": "83e0ef2f426dfad68f9b420304fc19414f8451a6bf24667f7e61c50e9ca57f14", "idempotency_key": "case-summary-v1", }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions/${sessionId}/files`, { method: "POST", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, "Content-Type": "application/json", }, body: JSON.stringify({ "filename": "case-summary.pdf", "media_type": "application/pdf", "size_bytes": 48213, "sha256": "83e0ef2f426dfad68f9b420304fc19414f8451a6bf24667f7e61c50e9ca57f14", "idempotency_key": "case-summary-v1" }), }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="201 Created" { "id": "file_b172c9", "filename": "case-summary.pdf", "media_type": "application/pdf", "size_bytes": 48213, "sha256": "83e0ef2f426dfad68f9b420304fc19414f8451a6bf24667f7e61c50e9ca57f14", "status": "prepared", "content_url": "https://api.tanomis.com/v1/sessions/ses_7d4f9c/files/file_b172c9/content", "created_at": "2026-09-02T11:45:00Z" } ``` --- Source: /api/files/upload-file-content # Upload file bytes PUT /v1/sessions/{session_id}/files/{file_id}/content: Upload the prepared file's bytes with `Content-Type: application/octet-stream` and a `Content-Length` equal to the prepared `size_bytes`. PUT/v1/sessions/{session_id}/files/{file_id}/content Upload the prepared file's bytes with `Content-Type: application/octet-stream` and a `Content-Length` equal to the prepared `size_bytes`. The file becomes `ready` when the digest matches. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `session_id` | string | The session, as returned by Create a session. | | `file_id` | string | The file, as returned by Prepare a file. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | | `Content-Type` | yes | Send `application/octet-stream`. | ## Request body The raw bytes of the file, sent as `application/octet-stream`. ## Responses | Status | Meaning | |---|---| | `200` | The file, now `ready`. | | `404` | No such prepared file, or attachments are disabled. | | `409` | The agent version lacks file support, or the bytes did not match the prepared size and digest. | ## Request ```bash curl -X PUT "https://api.tanomis.com/v1/sessions/ses_7d4f9c/files/file_b172c9/content" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" \ -H "Content-Type: application/octet-stream" \ --data-binary @case-summary.pdf ``` ```python import httpx response = httpx.put( f"https://api.tanomis.com/v1/sessions/{session_id}/files/{file_id}/content", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, "Content-Type": "application/octet-stream", }, content=open("case-summary.pdf", "rb").read(), ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions/${sessionId}/files/${fileId}/content`, { method: "PUT", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, "Content-Type": "application/octet-stream", }, body: await readFile("case-summary.pdf"), }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="200 OK" { "id": "file_b172c9", "filename": "case-summary.pdf", "media_type": "application/pdf", "size_bytes": 48213, "status": "ready", "created_at": "2026-09-02T11:45:00Z" } ``` --- Source: /api/interactions/answer-interaction # Answer an interaction POST /v1/sessions/{session_id}/interactions/{interaction_key}/answer: Accept a durable human answer by the stable `interaction_key`. POST/v1/sessions/{session_id}/interactions/{interaction_key}/answer Accept a durable human answer by the stable `interaction_key`. `answer` is JSON: a string for a question, or the decision object requested by a permission interaction. A successful answer returns `202 Accepted` with status `answered`; delivery is asynchronous, so list the session's interactions again until the key's status is `delivered`. If another caller already answered, the API returns `409 Conflict`; list again to read the recorded answer. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `session_id` | string | The session, as returned by Create a session. | | `interaction_key` | string | The stable key from the interactions list. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Request body | Field | Type | Required | Description | |---|---|---|---| | `kind` | string | required | Must match the interaction's kind: `question` or `permission`. | | `answer` | any | required | A string for a question; the requested decision object for a permission. | ## Responses | Status | Meaning | |---|---| | `202` | The answer was accepted. | | `404` | No such interaction in this session. | | `409` | Another caller already answered this interaction. | ## Request ```bash curl -X POST "https://api.tanomis.com/v1/sessions/ses_7d4f9c/interactions/ihk_91f0/answer" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" \ -H "Content-Type: application/json" \ -d '{ "kind": "question", "answer": "Include the approved credit." }' ``` ```python import httpx response = httpx.post( f"https://api.tanomis.com/v1/sessions/{session_id}/interactions/{interaction_key}/answer", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, json={ "kind": "question", "answer": "Include the approved credit.", }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions/${sessionId}/interactions/${interactionKey}/answer`, { method: "POST", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, "Content-Type": "application/json", }, body: JSON.stringify({ "kind": "question", "answer": "Include the approved credit." }), }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="202 Accepted" { "interaction_key": "ihk_91f0", "run_id": "run_a12bc3", "kind": "question", "status": "answered" } ``` --- Source: /api/interactions/list-interactions # List interactions GET /v1/sessions/{session_id}/interactions: List the session's human questions and permission decisions. GET/v1/sessions/{session_id}/interactions List the session's human questions and permission decisions. Filter with `status=pending` to find what needs an answer. Durable interactions are enabled in hosted Tanomis; another environment may not enable them, and there is no capability field to probe today, so confirm support with that environment's administrator. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `session_id` | string | The session, as returned by Create a session. | ## Query parameters | Parameter | Type | Description | |---|---|---| | `status` | string | Only interactions in this status, for example `pending`. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Responses | Status | Meaning | |---|---| | `200` | The session's interactions. | ## Request ```bash curl -X GET "https://api.tanomis.com/v1/sessions/ses_7d4f9c/interactions" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` ```python import httpx response = httpx.get( f"https://api.tanomis.com/v1/sessions/{session_id}/interactions", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions/${sessionId}/interactions`, { method: "GET", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, }, }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="200 OK" { "interactions": [ { "interaction_key": "ihk_91f0", "run_id": "run_a12bc3", "kind": "question", "status": "pending", "payload": { "question": "Should I include the account credit?" }, "created_at": "2026-09-02T11:43:08Z", "expires_at": "2026-09-02T12:43:08Z" } ] } ``` --- Source: /api/runs/cancel-run # Cancel a run POST /v1/sessions/{session_id}/runs/{run_id}/cancel: Request cancellation of an active run. POST/v1/sessions/{session_id}/runs/{run_id}/cancel Request cancellation of an active run. Cancellation is asynchronous: follow the event stream for `run_cancelled` or read the run until its status is terminal. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `session_id` | string | The session, as returned by Create a session. | | `run_id` | string | The run, as returned by Start a run. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Responses | Status | Meaning | |---|---| | `202` | Cancellation was requested. | | `404` | No such run in this session. | | `409` | The run is already terminal. | ## Request ```bash curl -X POST "https://api.tanomis.com/v1/sessions/ses_7d4f9c/runs/run_a12bc3/cancel" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` ```python import httpx response = httpx.post( f"https://api.tanomis.com/v1/sessions/{session_id}/runs/{run_id}/cancel", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions/${sessionId}/runs/${runId}/cancel`, { method: "POST", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, }, }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="202 Accepted" { "id": "run_a12bc3", "session_id": "ses_7d4f9c", "status": "running", "started_at": "2026-09-02T11:43:02Z" } ``` --- Source: /api/runs/get-run # Read a run GET /v1/sessions/{session_id}/runs/{run_id}: Read one run. GET/v1/sessions/{session_id}/runs/{run_id} Read one run. The run resource is the authoritative status; after an event stream ends without a terminal frame, read it here before deciding whether to reconnect. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `session_id` | string | The session, as returned by Create a session. | | `run_id` | string | The run, as returned by Start a run. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Responses | Status | Meaning | |---|---| | `200` | The run. | | `404` | No such run in this session. | ## Request ```bash curl -X GET "https://api.tanomis.com/v1/sessions/ses_7d4f9c/runs/run_a12bc3" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` ```python import httpx response = httpx.get( f"https://api.tanomis.com/v1/sessions/{session_id}/runs/{run_id}", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions/${sessionId}/runs/${runId}`, { method: "GET", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, }, }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="200 OK" { "id": "run_a12bc3", "session_id": "ses_7d4f9c", "status": "running", "started_at": "2026-09-02T11:43:02Z" } ``` --- Source: /api/runs/list-runs # List runs GET /v1/sessions/{session_id}/runs: List the session's runs. GET/v1/sessions/{session_id}/runs List the session's runs. Terminal run states are `succeeded`, `failed`, and `cancelled`; failed runs may carry `error_code` and `error_message`. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `session_id` | string | The session, as returned by Create a session. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Responses | Status | Meaning | |---|---| | `200` | The session's runs. | ## Request ```bash curl -X GET "https://api.tanomis.com/v1/sessions/ses_7d4f9c/runs" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` ```python import httpx response = httpx.get( f"https://api.tanomis.com/v1/sessions/{session_id}/runs", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions/${sessionId}/runs`, { method: "GET", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, }, }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="200 OK" { "runs": [ { "id": "run_a12bc3", "session_id": "ses_7d4f9c", "status": "succeeded", "started_at": "2026-09-02T11:43:02Z" } ] } ``` --- Source: /api/runs/start-run # Start a run POST /v1/sessions/{session_id}/runs: Start one run inside an existing session. POST/v1/sessions/{session_id}/runs Start one run inside an existing session. The session serializes its runs. `idempotency_key` is scoped to the session and prevents a retry from starting a second run; a request without a key always creates a new run. By default the endpoint returns `202 Accepted` with the admitted run; set `Accept: text/event-stream` or add `?stream=true` to stream immediately, or reconnect later through the run's events endpoint. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `session_id` | string | The session, as returned by Create a session. | ## Query parameters | Parameter | Type | Description | |---|---|---| | `stream` | boolean | Stream the run's events instead of returning the admitted run. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Request body | Field | Type | Required | Description | |---|---|---|---| | `input` | object | required | Passed to the agent as JSON. The shape is defined by your agent's entrypoint. | | `idempotency_key` | string | optional | Scoped to the session; a retry with the same key returns the original run. At most 255 bytes. | | `attachments` | array | optional | Files prepared and uploaded in this session, as `{ "file_id": ... }` objects. | | `work` | object | optional | Settlement-mode agents only. Send either `subject` (open new work) or `work_id` (continue work opened by an earlier session). | | `work.subject` | string | optional | Opens new work, for example `invoice:INV-1842`. | | `work.work_id` | string | optional | Continues work opened by an earlier session. | | `work.contract_ref` | string | optional | Sent together with `contract_version` when opening work. Cannot be added or changed later. | | `work.contract_version` | string | optional | Sent together with `contract_ref`. | ## Responses | Status | Meaning | |---|---| | `200` | With streaming requested: the run's server-sent event stream. | | `202` | The run was admitted. Poll it or open the event stream. | | `404` | No such session in the selected tenant. | | `409` | The idempotency key matched a run with a different body, or the session cannot admit a run. | ## Request ```bash curl -X POST "https://api.tanomis.com/v1/sessions/ses_7d4f9c/runs" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" \ -H "Content-Type: application/json" \ -d '{ "input": { "prompt": "Draft a concise reply for the customer." }, "idempotency_key": "support-case-1842-reply-1" }' ``` ```python import httpx response = httpx.post( f"https://api.tanomis.com/v1/sessions/{session_id}/runs", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, json={ "input": { "prompt": "Draft a concise reply for the customer.", }, "idempotency_key": "support-case-1842-reply-1", }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions/${sessionId}/runs`, { method: "POST", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, "Content-Type": "application/json", }, body: JSON.stringify({ "input": { "prompt": "Draft a concise reply for the customer." }, "idempotency_key": "support-case-1842-reply-1" }), }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="202 Accepted" { "id": "run_a12bc3", "session_id": "ses_7d4f9c", "status": "running", "started_at": "2026-09-02T11:43:02Z" } ``` ## Attach files Reference files that were prepared and uploaded in this session (see Prepare a file) when starting the run. ```json title="Request" { "input": { "prompt": "Summarize the attached case." }, "attachments": [ { "file_id": "file_b172c9" } ], "idempotency_key": "case-summary-run-v1" } ``` ## Settlement-mode agents For a settlement-mode agent, the first run in a new session must declare the work it serves. Use `work.subject` to open new work, or `work.work_id` to continue work opened by an earlier session; send either, never both. When opening work, `contract_ref` and `contract_version` may establish a contract and must be sent together. Later requests may omit the contract or restate that exact pair, but cannot add or change it. The admitted run includes `settlement_work_id`; store it if another session may need to continue the same work. Standard-mode agents do not require a `work` object. Durable work and settlement are tenant capabilities: for a version in `settlement` mode, an unavailable settlement boundary fails session activation rather than silently running without it. ```json title="Request" { "input": { "prompt": "Reconcile invoice INV-1842." }, "work": { "subject": "invoice:INV-1842", "contract_ref": "billing/reconciliation", "contract_version": "2026-09" }, "idempotency_key": "invoice-1842-attempt-1" } ``` ```json title="Response" { "id": "run_a12bc3", "session_id": "ses_7d4f9c", "status": "running", "started_at": "2026-09-02T11:43:02Z", "settlement_work_id": "work_f61ca9" } ``` --- Source: /api/runs/stream-run-events # Stream run events GET /v1/sessions/{session_id}/runs/{run_id}/events: Stream or replay the run's events as server-sent events. GET/v1/sessions/{session_id}/runs/{run_id}/events Stream or replay the run's events as server-sent events. Each frame carries an SSE `id`, an `event` name, and the complete event object as JSON in `data`. Terminal event types are `run_completed`, `run_cancelled`, and `error`. To reconnect after a disconnect, send `Last-Event-ID` with the last id you processed. Replay starts strictly after `Last-Event-ID` when live or persisted history is available. Event ids order events only within one run: treat them as opaque strings, store them with the session and run ids, and deduplicate by run id plus event id. A stream can close cleanly without a terminal frame when no events are available; read the run resource for its authoritative status and reconnect only while it remains non-terminal. Ignore unknown event names and fields. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `session_id` | string | The session, as returned by Create a session. | | `run_id` | string | The run, as returned by Start a run. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | | `Accept` | yes | Send `text/event-stream`. | | `Last-Event-ID` | no | Send `14`. | ## Responses | Status | Meaning | |---|---| | `200` | Server-sent events. | | `404` | No such run in this session. | ## Request ```bash curl -X GET "https://api.tanomis.com/v1/sessions/ses_7d4f9c/runs/run_a12bc3/events" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" \ -H "Accept: text/event-stream" \ -H "Last-Event-ID: 14" ``` ```python import httpx response = httpx.get( f"https://api.tanomis.com/v1/sessions/{session_id}/runs/{run_id}/events", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, "Accept": "text/event-stream", "Last-Event-ID": "14", }, ) for line in response.iter_lines(): print(line) ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions/${sessionId}/runs/${runId}/events`, { method: "GET", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, "Accept": "text/event-stream", "Last-Event-ID": "14", }, }); const reader = response.body.getReader(); // Decode server-sent events from the stream; see Stream and replay events. ``` ## Response ```text title="200 OK · text/event-stream" id: 14 event: message_delta data: {"id":"14","type":"message_delta","session_id":"ses_7d4f9c","run_id":"run_a12bc3","created_at":"2026-09-03T09:00:00.014Z","data":{"text":"I found"}} ``` --- Source: /api/sessions/create-session # Create a session POST /v1/sessions: Create a reusable session for one enabled agent. POST/v1/sessions Create a reusable session for one enabled agent. A new session returns `201 Created`. While that session is non-terminal, replaying the same `idempotency_key` with the same provisioning inputs returns it with `200 OK`; the same key with different inputs returns `409 Conflict`. Once the original session is stopped, failed, or expired, replay returns `412 Precondition Failed` instead of creating a replacement. Generated from the Platform API specification on 2026-09-04. ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Request body | Field | Type | Required | Description | |---|---|---|---| | `agent_name` | string | required | An agent enabled for the selected tenant. | | `idempotency_key` | string | optional | Replays the same session for the same caller and request. At most 255 bytes. | | `model` | string | optional | A model allowed by the deployed agent version. | | `agent_inputs` | object | optional | Agent-specific initialization data. | ## Responses | Status | Meaning | |---|---| | `200` | An idempotent replay returned the existing non-terminal session. | | `201` | The session was created. | | `409` | The idempotency key matched a session created with different provisioning inputs. | | `412` | The idempotency key refers to a session that is already terminal. | ## Request ```bash curl -X POST "https://api.tanomis.com/v1/sessions" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" \ -H "Content-Type: application/json" \ -d '{ "agent_name": "support-copilot", "idempotency_key": "support-case-1842" }' ``` ```python import httpx response = httpx.post( f"https://api.tanomis.com/v1/sessions", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, json={ "agent_name": "support-copilot", "idempotency_key": "support-case-1842", }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions`, { method: "POST", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, "Content-Type": "application/json", }, body: JSON.stringify({ "agent_name": "support-copilot", "idempotency_key": "support-case-1842" }), }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="201 Created" { "id": "ses_7d4f9c", "agent_name": "support-copilot", "version": "0.4.0", "status": "ready", "model": "claude-sonnet", "created_at": "2026-09-02T11:42:18Z", "awaiting_input": false } ``` --- Source: /api/sessions/get-session # Read a session GET /v1/sessions/{session_id}: Read one session, including `awaiting_input` and, where file attachments are enabled, `session_files_supported`. GET/v1/sessions/{session_id} Read one session, including `awaiting_input` and, where file attachments are enabled, `session_files_supported`. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `session_id` | string | The session, as returned by Create a session. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Responses | Status | Meaning | |---|---| | `200` | The session. | | `404` | No such session in the selected tenant. | ## Request ```bash curl -X GET "https://api.tanomis.com/v1/sessions/ses_7d4f9c" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` ```python import httpx response = httpx.get( f"https://api.tanomis.com/v1/sessions/{session_id}", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions/${sessionId}`, { method: "GET", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, }, }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="200 OK" { "id": "ses_7d4f9c", "agent_name": "support-copilot", "version": "0.4.0", "status": "ready", "model": "claude-sonnet", "created_at": "2026-09-02T11:42:18Z", "awaiting_input": false } ``` --- Source: /api/sessions/list-sessions # List sessions GET /v1/sessions: List the caller's sessions in the selected tenant. GET/v1/sessions List the caller's sessions in the selected tenant. `include_all_users=true` returns the tenant-wide view permitted by the caller's role, inside the same tenant boundary. Generated from the Platform API specification on 2026-09-04. ## Query parameters | Parameter | Type | Description | |---|---|---| | `agent` | string | Only sessions of this agent. | | `status` | string | Only sessions in this status. | | `limit` | integer | Maximum number of sessions to return. | | `parent_session_id` | string | Only sessions forked from this parent. | | `include_all_users` | boolean | Include sessions owned by other users the caller's role may see. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Responses | Status | Meaning | |---|---| | `200` | Sessions, newest first. | ## Request ```bash curl -X GET "https://api.tanomis.com/v1/sessions" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` ```python import httpx response = httpx.get( f"https://api.tanomis.com/v1/sessions", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions`, { method: "GET", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, }, }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="200 OK" { "sessions": [ { "id": "ses_7d4f9c", "agent_name": "support-copilot", "version": "0.4.0", "status": "ready", "created_at": "2026-09-02T11:42:18Z", "awaiting_input": false } ] } ``` --- Source: /api/sessions/resume-session # Resume a session POST /v1/sessions/{session_id}/resume: Restart a stopped, failed, or expired session. POST/v1/sessions/{session_id}/resume Restart a stopped, failed, or expired session. Suspended live sessions resume automatically when work arrives, so this is only needed after a terminal state. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `session_id` | string | The session, as returned by Create a session. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Responses | Status | Meaning | |---|---| | `200` | The session, ready again. | | `404` | No such session in the selected tenant. | | `409` | The session is not in a state that can be resumed. | ## Request ```bash curl -X POST "https://api.tanomis.com/v1/sessions/ses_7d4f9c/resume" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` ```python import httpx response = httpx.post( f"https://api.tanomis.com/v1/sessions/{session_id}/resume", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions/${sessionId}/resume`, { method: "POST", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, }, }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="200 OK" { "id": "ses_7d4f9c", "agent_name": "support-copilot", "version": "0.4.0", "status": "ready", "model": "claude-sonnet", "created_at": "2026-09-02T11:42:18Z", "awaiting_input": false } ``` --- Source: /api/sessions/stop-session # Stop a session POST /v1/sessions/{session_id}/stop: Stop the session and release its active runtime. POST/v1/sessions/{session_id}/stop Stop the session and release its active runtime. Stop it when no more work will use it. A stopped session can be restarted with Resume a session. Generated from the Platform API specification on 2026-09-04. ## Path parameters | Parameter | Type | Description | |---|---|---| | `session_id` | string | The session, as returned by Create a session. | ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Responses | Status | Meaning | |---|---| | `200` | The session in its new state. | | `404` | No such session in the selected tenant. | ## Request ```bash curl -X POST "https://api.tanomis.com/v1/sessions/ses_7d4f9c/stop" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` ```python import httpx response = httpx.post( f"https://api.tanomis.com/v1/sessions/{session_id}/stop", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/sessions/${sessionId}/stop`, { method: "POST", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, }, }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="200 OK" { "id": "ses_7d4f9c", "agent_name": "support-copilot", "version": "0.4.0", "status": "stopped", "created_at": "2026-09-02T11:42:18Z", "awaiting_input": false } ``` --- Source: /api/workspaces/list-workspaces # List workspaces GET /v1/me/workspaces: Return only the workspace projections granted to the caller. GET/v1/me/workspaces Return only the workspace projections granted to the caller. Each combines an organization, an attached tenant, the caller's role, and effective capabilities. Copy `organization_id` and the selected nested `tenant_id` into the two workspace headers. Selecting the projection supplies context; the workspace does not own the tenant or its resources. An authenticated caller with no active organization membership receives `{ "workspaces": [] }`. A developer or viewer who belongs to an organization but has no granted tenant remains visible as that organization with `"tenants": []`; `tanomis workspace ls` renders its tenant as `(none)`. Owners and administrators see the organization's attached tenants. Generated from the Platform API specification on 2026-09-04. ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Responses | Status | Meaning | |---|---| | `200` | The caller's projections. | ## Request ```bash curl -X GET "https://api.tanomis.com/v1/me/workspaces" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` ```python import httpx response = httpx.get( f"https://api.tanomis.com/v1/me/workspaces", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/me/workspaces`, { method: "GET", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, }, }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="200 OK" { "workspaces": [ { "organization_id": "org_abc123", "organization_slug": "northwind", "organization_display_name": "Northwind", "role": "developer", "tenants": [ { "tenant_id": "customer-production", "state": "ready", "is_default": true, "capabilities": [ "run", "view" ] } ] } ] } ``` --- Source: /api/workspaces/whoami # Inspect the caller GET /v1/whoami: Confirm which identity and capabilities the access token resolved. GET/v1/whoami Confirm which identity and capabilities the access token resolved. `scopes` lists the caller's own granted API scopes; an empty array means none. `file_attachments_enabled` is environment-wide; a session's `session_files_supported` is the additional agent-specific check. Generated from the Platform API specification on 2026-09-04. ## Headers | Header | Required | Description | |---|---|---| | `Authorization` | yes | `Bearer` plus a short-lived OAuth access token issued for the Tanomis API audience. Backends obtain tokens with an [organization automation client](/guides/run/authenticate-your-backend); the CLI's human login is not an application credential. | | `X-Organization-ID` | yes | The `organization_id` from the workspace projection you selected in `GET /v1/me/workspaces`. | | `X-Workspace-Tenant-ID` | yes | The nested `tenant_id` from the same projection. A header cannot widen what the token grants. Some single-workspace or self-hosted environments take `X-Tenant-ID` instead, as supplied by their administrator. | ## Responses | Status | Meaning | |---|---| | `200` | The resolved identity. | ## Request ```bash curl -X GET "https://api.tanomis.com/v1/whoami" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` ```python import httpx response = httpx.get( f"https://api.tanomis.com/v1/whoami", headers={ "Authorization": f"Bearer {token}", "X-Organization-ID": organization_id, "X-Workspace-Tenant-ID": tenant_id, }, ) response.raise_for_status() result = response.json() ``` ```typescript const response = await fetch(`https://api.tanomis.com/v1/whoami`, { method: "GET", headers: { "Authorization": `Bearer ${token}`, "X-Organization-ID": organizationId, "X-Workspace-Tenant-ID": tenantId, }, }); if (!response.ok) throw new Error(`${response.status} ${await response.text()}`); const result = await response.json(); ``` ## Response ```json title="200 OK" { "subject": "usr_84af2c", "tenant_id": "customer-production", "is_admin": false, "scopes": [], "file_attachments_enabled": true } ``` --- Source: /changelog # Changelog Platform, CLI, manifest, and API changes that affect what you build on Tanomis, newest first. Changes that affect what you build, newest first. Each entry names the surface it touches and links to the page that describes it. ## September 2026 ### 8 September - **CLI.** Tanomis v0.2.0 renames the command to `tanomis`. The installer includes a deprecated `cog` shim; existing `COG_*` settings and the legacy config directory remain supported. New installs use `TANOMIS_*` settings and the Tanomis config directory. See [Install Tanomis](/getting-started/install). - **Docs and domains.** Developer docs and downloads move to `docs.tanomis.com` and `get.tanomis.com`. Old Cogward docs and download links redirect to the same path on Tanomis; previously published `cog/` release objects remain unchanged. - **Coding agents.** Register the bundled docs MCP server with `tanomis skills install`, or run it directly with `tanomis docs serve`. See [Use with coding agents](/getting-started/coding-agents). - **CLI.** `cog` v0.1.1 adds the platform skills for coding agents and the documentation server. `cog skills install` writes four skills (build, deploy and roll out, run and debug, govern) for Claude Code, Codex, and Cursor and registers `cog docs serve`, an MCP server over these docs. See [Build with a coding agent](/getting-started/coding-agents). ### 6 September - **CLI.** `cog` v0.1.0 is the first public release. Install on macOS and Linux with `curl -fsSL https://get.cogward.com/install.sh | sh`, on Windows with `irm https://get.cogward.com/install.ps1 | iex`. Every release carries `SHA256SUMS` and a keyless cosign signature. See [Install cog](/getting-started/install). ### 5 September - **Platform.** Organization-owned automation credentials ship: create a machine client in the Console or with `cog m2m create`, choose the `observe`, `invoke`, or `deploy` profile, rotate and revoke. See [Automation credentials](/guides/deploy/automation-credentials) and [Authenticate a backend](/guides/run/authenticate-your-backend). - **CLI.** `cog m2m` manages machine clients; `cog agent deploy --scope global --tenants all` registers an agent once and enables it for every tenant. See the [Quickstart](/getting-started/quickstart). - **Docs.** The site is reorganized around the agent journey: a new Overview, [Quickstart](/getting-started/quickstart), [Platform model](/concepts/platform-model), and [What is available where](/reference/availability). Pages marked **Soon** describe capabilities being built. ### 4 September - **CLI.** `cog login` can select a workspace, and `cog workspace show` prints the selection. See [Authenticate and select a workspace](/getting-started/authenticate-and-select-workspace). - **Platform.** Durable questions and permission requests pause a run until a person answers through the Platform API. See [Approval-required actions](/guides/run/approval-required-actions). - **Platform.** Agent identity modes and the authorization contract are documented. See [Identity and access](/concepts/identity-and-access). ### 2 September - **API.** The customer Platform API reference is published: sessions, runs, event streaming, human interactions, and session files. See [Platform API](/api). ## August 2026 ### 29 August - **Manifest.** `network.egress` declares the destinations a version may reach, bounded by the tenant ceiling. See [Agent manifest](/reference/agent-manifest#network). ### 28 August - **Manifest.** The complete `agent.yaml` field reference is published, including cross-field rules and pre-build validation. See [Agent manifest](/reference/agent-manifest). --- Source: /cli # Journey commands A focused reference for the tanomis commands used to authenticate, scaffold, deploy, inspect, and run. This is the compact reference for the first developer journey. Run `tanomis --help` for the complete flag set supported by your installed version. ## Install ```bash curl -fsSL https://get.tanomis.com/install.sh | sh ``` One command on macOS and Linux; Windows uses `irm https://get.tanomis.com/install.ps1 | iex`. Pin with `TANOMIS_VERSION`; see [Install tanomis](/getting-started/install). ## Authenticate ```bash tanomis login --console https://api.tanomis.com --name saas tanomis auth status tanomis logout ``` `login --console` discovers the hosted configuration, creates or updates the named context, and starts device authorization by default. `logout` clears cached credentials but keeps the context. Follow [Authenticate and select a workspace](/getting-started/authenticate-and-select-workspace) for the complete human login, context, workspace-discovery, selection, and failure flow. ## Discover and scaffold ```bash tanomis agent harnesses tanomis agent new NAME \ --harness claude-agents-sdk \ --agents-root ./agents \ --description "What this agent does" ``` `--harness` accepts a value reported by `agent harnesses`. `--agents-root` can point anywhere, including an external repository. Scaffolding writes a self-contained project and runs `uv lock` when `uv` is available. ## Validate and deploy ```bash tanomis agent deploy PATH --tenant TENANT --dry-run tanomis agent deploy PATH --tenant TENANT tanomis agent deploy PATH --scope global --tenants all ``` `--tenant` deploys to one tenant. `--scope global --tenants all` registers a global agent once and enables it for every active tenant; a platform operator's path. Useful deploy flags: | Flag | Meaning | |---|---| | `--dry-run` | Preflight and package locally; create no build and upload nothing. | | `--version VERSION` | Override the version from `agent.yaml`. | | `--no-wait` | Return after upload instead of waiting for terminal build status. | | `--no-stream-logs` | Poll status without streaming build events. | | `--output json` | Emit machine-readable output for automation. | The supported remote build profile is `python-managed`. Remote builds require `uv.lock` or a `requirements.txt`; pin every requirement to keep builds reproducible. The CLI verifies that the file exists but does not inspect its entries. `--allow-unlocked` is not supported. ## Inspect ```bash tanomis agent ls --tenant TENANT tanomis agent get NAME --tenant TENANT tanomis agent versions NAME --tenant TENANT tanomis agent versions NAME --by-tenant ``` Use `get` for an agent-centric view and `versions --by-tenant` for the reverse rollout view. ## Roll out and roll back ```bash tanomis agent rollout NAME --version VERSION --tenants TENANT --dry-run tanomis agent rollout NAME --version VERSION --tenants TENANT --canary-share 10 tanomis agent versions NAME --by-tenant ``` `rollout` promotes the requested global version if needed, then assigns it only to the requested tenants. Omit `--canary-share` for a baseline assignment; a value from 1 through 99 uses each tenant's baseline observed at command start and adds the version as a canary. Serialize assignment changes for each tenant so a cached baseline cannot overwrite a concurrent rollout. Treat output as provisional: a tenant assignment failure can appear as success. Only a follow-up `versions --by-tenant` read verifies the assignment. Roll back with named-tenant `agent enable --baseline KNOWN_GOOD`; built-in deprecated versions remain serveable even though `rollout` cannot transition them back to production. Require zero failed in the enable summary and verify the version view. Comma-separated tenant batches require ordinary authority over every target; support act-as assertions are tenant-bound, so run one tenant per command under its own assertion. `agent disable` removes enablement and stops service; it does not select an older version. Follow the [rollout and rollback tutorial](/guides/deploy/rollout-and-rollback) before changing customer tenant assignments. ## Run ```bash tanomis session run NAME \ --tenant TENANT \ --prompt "Your prompt" ``` `session run` creates a session, starts a run, and streams events through the managed runtime. Use `--input '{"key":"value"}'` for raw JSON instead of `--prompt`; the two flags are mutually exclusive. Use `--no-stream` to print the run ID without following events. ## Automate ```bash tanomis m2m create ci-deployer --profile deploy tanomis m2m ls tanomis m2m rotate ci-deployer ``` `m2m create` prints the client ID and secret once. Set `TANOMIS_API_URL`, `TANOMIS_CLIENT_ID`, and `TANOMIS_CLIENT_SECRET` in a job and every command above runs unattended; see [Automation credentials](/guides/deploy/automation-credentials). ## Command-scoped context The active context applies automatically. Select another existing context for one command without switching: ```bash tanomis agent ls --context staging --tenant TENANT ``` `--context` selects an existing context. `login --name` names the context created by `login --console`. --- Source: /cli/agent # tanomis agent Reference for tanomis agent: author, register, and inspect agents. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis agent --help for the version you have installed. ## Usage ```text title="Usage" tanomis agent ``` ## Description Author, register, and inspect agents. ## Commands | Command | Description | |---|---| | [`tanomis agent delete-local`](/cli/agent/delete-local) | delete a scaffolded agent directory from disk | | [`tanomis agent deploy`](/cli/agent/deploy) | package, upload, and remotely build an agent from local source | | [`tanomis agent disable`](/cli/agent/disable) | remove an agent's enablement for specific tenants (inverse of enable) | | [`tanomis agent enable`](/cli/agent/enable) | enable a global agent for tenants | | [`tanomis agent get`](/cli/agent/get) | show the agent and versions served in the selected workspace | | [`tanomis agent harnesses`](/cli/agent/harnesses) | list available agent harnesses | | [`tanomis agent inventory`](/cli/agent/inventory) | list the per-version inventory (AI-BOM) of the agents a tenant can see | | [`tanomis agent ls`](/cli/agent/ls) | list registered agents (or, with `--tenant`, the agents a tenant can run) | | [`tanomis agent new`](/cli/agent/new) | scaffold a new agent from a harness template | | [`tanomis agent promote`](/cli/agent/promote) | promote a version to production (alias for transition `--to` production) | | [`tanomis agent rm`](/cli/agent/rm) | soft-retire one scope-qualified agent from the registry | | [`tanomis agent rollout`](/cli/agent/rollout) | roll a version out to specific tenants (promote-if-needed, then enable) | | [`tanomis agent statuses`](/cli/agent/statuses) | show the version lifecycle: statuses and their allowed transitions | | [`tanomis agent versions`](/cli/agent/versions) | list an agent's version lineage (label, id, status, kind) |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/agent/delete-local # tanomis agent delete-local Reference for tanomis agent delete-local: delete a scaffolded agent directory from disk. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis agent delete-local --help for the version you have installed. ## Usage ```text title="Usage" tanomis agent delete-local NAME [flags] ``` ## Description Delete a scaffolded agent directory from disk. ## Flags | Flag | Type | Description | |---|---|---| | `--agents-root` | string | Local agents directory. Default `dev/agents`. | | `--yes` | bool | Skip the confirmation prompt. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/agent/deploy # tanomis agent deploy Reference for tanomis agent deploy: package, upload, and remotely build an agent from local source. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis agent deploy --help for the version you have installed. ## Usage ```text title="Usage" tanomis agent deploy [PATH] [flags] ``` ## Description Turn local Python agent source into a remote build (feature 260703d1d). Preflights PATH (default "."): parses agent.yaml, requires a harness (framework), rejects a stray Dockerfile or an unsupported top-level build field, and flags secret-looking files (.env, *.pem, *.key, or a name containing "credentials") — those are excluded from the package as well as warned about. Packages PATH deterministically (a repackage of the same tree yields an identical checksum), enforces the Python lock policy (a remote build requires a uv.lock or a pinned requirements.txt; a bare pyproject.toml is rejected), then creates a build job, uploads the bundle, and streams its progress to the terminal. `--dry-run` stops after packaging and prints the plan: no build is created and no bytes are uploaded. Public egress additionally performs an authenticated read-only ceiling validation. `--stage` builds and registers the version, ready to start sessions, and enables it for nobody: the tenant keeps the version it serves. Test it with `tanomis eval ci`, then release it with `tanomis agent promote`, naming the version and the tenant. ## Flags | Flag | Type | Description | |---|---|---| | `--allow-unlocked` | bool | Not supported for a remote build: the build service requires a uv.lock or a pinned requirements.txt (using this flag now returns a clear error rather than failing the remote build). | | `--build-profile` | string | Build profile (only python-managed is supported in v1). Default `python-managed`. | | `--dry-run` | bool | Preflight and package without creating a build or uploading source; public egress validates the ceiling. | | `--force-rebuild` | bool | Reserved for a future server-side build cache; currently a no-op. | | `--name` | string | Agent name (default: agent.yaml's name). | | `--no-stream-logs` | bool | Disable streaming build events (poll for terminal status instead). | | `--no-wait` | bool | Return immediately after the source uploads, without waiting for the build. | | `--scope` | string | Registration scope: organization, workspace or platform (default: organization where the platform enables it, else workspace). | | `--stage` | bool | Build and register the version without enabling it for the tenant (test it before release). | | `--stream-logs` | bool | Stream build events to the terminal (default: on when stdout is a terminal). Default `true`. | | `--tenants` | string | Platform deployment targets: all or a comma-separated tenant list (requires `--scope` platform). | | `--version` | string | Version label (default: agent.yaml's version). | | `--wait` | bool | Wait for the build to reach a terminal status before exiting. Default `true`. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
## Examples --- Source: /cli/agent/disable # tanomis agent disable Reference for tanomis agent disable: remove an agent's enablement for specific tenants (inverse of enable). Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis agent disable --help for the version you have installed. ## Usage ```text title="Usage" tanomis agent disable NAME [flags] ``` ## Description Stop serving an agent to the named tenants by removing their enablement rows — the inverse of `tanomis agent enable`. `--tenants` is a comma-separated list. There is no 'all' (removing every tenant's enablement must be explicit). A tenant that isn't enabled is reported and skipped, so re-running is safe (idempotent). The agent and its versions are untouched; only the per-tenant enablement is removed (an audit event is recorded). ## Flags | Flag | Type | Description | |---|---|---| | `--by` | string | Identity recorded as the principal. Default `local-dev`. | | `--tenants` | string | Comma-separated tenant ids to disable (no 'all'). |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/agent/enable # tanomis agent enable Reference for tanomis agent enable: enable a global agent for tenants. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis agent enable --help for the version you have installed. ## Usage ```text title="Usage" tanomis agent enable NAME [flags] ``` ## Description Enable one global agent across a set of tenants in a single call. `--tenants` takes a comma-separated list of tenant ids, or 'all' for every tenant. ## Flags | Flag | Type | Description | |---|---|---| | `--baseline` | string | Baseline version to enable — label (v1) or id (ver_…). | | `--canary` | string | Optional canary version — label (v1) or id (ver_…). | | `--canary-share` | int | Canary traffic share (0-100). | | `--tenants` | string | Comma-separated tenant ids, or 'all'. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/agent/get # tanomis agent get Reference for tanomis agent get: show the agent and versions served in the selected workspace. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis agent get --help for the version you have installed. ## Usage ```text title="Usage" tanomis agent get NAME ``` ## Description Show the registration and baseline/canary versions served in the selected workspace. In the platform context, show the platform registration, full version lineage, and tenant enablements.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/agent/harnesses # tanomis agent harnesses Reference for tanomis agent harnesses: list available agent harnesses. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis agent harnesses --help for the version you have installed. ## Usage ```text title="Usage" tanomis agent harnesses ``` ## Description List available agent harnesses.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/agent/inventory # tanomis agent inventory Reference for tanomis agent inventory: list the per-version inventory (AI-BOM) of the agents a tenant can see. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis agent inventory --help for the version you have installed. ## Usage ```text title="Usage" tanomis agent inventory [flags] ``` ## Description List every agent version the tenant can see with its models, image digest, harness, integrations, tools, golden fingerprint and recorded signature and build provenance. A tenant-owned agent lists its whole lineage; a global agent lists only the versions the tenant is served. A field the registry does not record is null. With `--output` json the output is an export document stamped with schema_version, generated_at and tenant. ## Flags | Flag | Type | Description | |---|---|---| | `--tenant` | string | Tenant whose inventory to list (default: the selected workspace). |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--verbose` | bool | Verbose output. |
--- Source: /cli/agent/ls # tanomis agent ls Reference for tanomis agent ls: list registered agents (or, with --tenant, the agents a tenant can run). Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis agent ls --help for the version you have installed. ## Usage ```text title="Usage" tanomis agent ls [flags] ``` ## Description List registered agents (or, with `--tenant`, the agents a tenant can run). Aliases: `list`. ## Flags | Flag | Type | Description | |---|---|---| | `--tenant` | string | Show the agents this tenant can run (global-enabled + tenant-scoped), with served version. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--verbose` | bool | Verbose output. |
--- Source: /cli/agent/new # tanomis agent new Reference for tanomis agent new: scaffold a new agent from a harness template. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis agent new --help for the version you have installed. ## Usage ```text title="Usage" tanomis agent new NAME [flags] ``` ## Description Scaffold a new agent from a harness template into ``/``. `--agents-root` defaults to dev/agents (the monorepo convention) but accepts any path, absolute or relative, including a directory outside this repo (e.g. an external agent repo cloned on its own). The generated agent is self-contained (agent.yaml, Python source, pyproject.toml, README, .gitignore, .tanomisignore) and has no monorepo-only assumptions, so it deploys with 'tanomis agent deploy' unmodified. The manifest is production-ready as generated: it carries a live authorization contract (a scope_ceiling declaring the agent's own model, plus allowed_audiences). Golden snapshots are platform-managed to accelerate startup. Dependencies are locked with uv when it is available, which a remote build requires. ## Flags | Flag | Type | Description | |---|---|---| | `--agents-root` | string | Target agents directory; any path, including one outside this repo. Default `dev/agents`. | | `--description` | string | Manifest description. | | `--harness` | string | Agent harness/framework. Default `claude-agents-sdk`. | | `--model` | string | Model id. | | `--overwrite` | bool | Overwrite an existing agent directory. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
## Examples --- Source: /cli/agent/promote # tanomis agent promote Reference for tanomis agent promote: promote a version to production (alias for transition --to production). Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis agent promote --help for the version you have installed. ## Usage ```text title="Usage" tanomis agent promote NAME [flags] ``` ## Description Promote a version to production (alias for transition `--to` production). ## Flags | Flag | Type | Description | |---|---|---| | `--by` | string | Identity recorded as the principal. Default `local-dev`. | | `--scope-tenant` | string | Select the tenant-scoped agent of this name (default: the global agent). | | `--version` | string | Version label to promote. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/agent/rm # tanomis agent rm Reference for tanomis agent rm: soft-retire one scope-qualified agent from the registry. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis agent rm --help for the version you have installed. ## Usage ```text title="Usage" tanomis agent rm NAME [flags] ``` ## Description Soft-retire exactly one agent registration while preserving its audit history. The scope selector is mandatory because global and tenant-scoped agents may share a name. Use `--scope` global, or `--scope` tenant with `--scope-tenant-id` TENANT. Aliases: `remove`. ## Flags | Flag | Type | Description | |---|---|---| | `--scope` | string | Registration scope to retire: global or tenant (required). | | `--scope-tenant-id` | string | Owning tenant id (required with `--scope` tenant). | | `--yes` | bool | Skip the confirmation prompt. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/agent/rollout # tanomis agent rollout Reference for tanomis agent rollout: roll a version out to specific tenants (promote-if-needed, then enable). Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis agent rollout --help for the version you have installed. ## Usage ```text title="Usage" tanomis agent rollout NAME [flags] ``` ## Description Roll one version out to an explicit set of tenants in a single, tenant-scoped step. rollout composes the two underlying operations so the operator's intent — "serve version V to these tenants" — is one command: it first ensures the version is at the production status (transitioning it if needed, surfacing any gate failure verbatim), then enables it for the named tenants only. By default the version becomes the tenants' baseline; with `--canary-share` N (1-99) it is added as a canary at that share, preserving each tenant's existing baseline. Other tenants are never touched. Use `--dry-run` to preview the per-tenant before/after plan without changing anything. This is porcelain over `tanomis agent promote` + `tanomis agent enable`, which remain available. ## Flags | Flag | Type | Description | |---|---|---| | `--by` | string | Identity recorded as the principal. Default `local-dev`. | | `--canary-share` | int | 0 = baseline (default); 1-99 = roll out as canary at this share. | | `--dry-run` | bool | Print the per-tenant before/after plan without making changes. | | `--tenants` | string | Comma-separated tenant ids, or 'all'. | | `--version` | string | Version label to roll out (e.g. v2). |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
## Examples --- Source: /cli/agent/statuses # tanomis agent statuses Reference for tanomis agent statuses: show the version lifecycle: statuses and their allowed transitions. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis agent statuses --help for the version you have installed. ## Usage ```text title="Usage" tanomis agent statuses ``` ## Description Show the promotion state machine — every version status, the statuses it can transition to (what `tanomis agent transition `--to`` accepts), and whether each gates promotion, serves traffic, or is terminal. Includes built-in and any operator-added custom statuses. Aliases: `lifecycle`.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/agent/versions # tanomis agent versions Reference for tanomis agent versions: list an agent's version lineage (label, id, status, kind). Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis agent versions --help for the version you have installed. ## Usage ```text title="Usage" tanomis agent versions NAME [flags] ``` ## Description List an agent's immutable version lineage. With `--by-tenant`, render the version-centric rollout view instead: for each version, the tenants that serve it as baseline and as canary. This is the reverse of `tanomis agent get` (agent→tenants) and `tanomis agent ls `--tenant`` (tenant→agents), and answers "who is on v2?" directly. ## Flags | Flag | Type | Description | |---|---|---| | `--by-tenant` | bool | Show, per version, which tenants serve it as baseline/canary. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/auth # tanomis auth Reference for tanomis auth: inspect authentication state. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis auth --help for the version you have installed. ## Usage ```text title="Usage" tanomis auth ``` ## Description Inspect authentication state. ## Commands | Command | Description | |---|---| | [`tanomis auth status`](/cli/auth/status) | show the active context's auth state |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/auth/status # tanomis auth status Reference for tanomis auth status: show the active context's auth state. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis auth status --help for the version you have installed. ## Usage ```text title="Usage" tanomis auth status ``` ## Description Show the active context's auth state.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/config # tanomis config Reference for tanomis config: manage tanomis configuration and contexts. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis config --help for the version you have installed. ## Usage ```text title="Usage" tanomis config ``` ## Description Manage tanomis configuration and contexts. ## Commands | Command | Description | |---|---| | [`tanomis config current-context`](/cli/config/current-context) | print the active context name | | [`tanomis config delete-context`](/cli/config/delete-context) | remove a context and clear its cached credentials | | [`tanomis config use-context`](/cli/config/use-context) | set the active context | | [`tanomis config view`](/cli/config/view) | show the effective configuration |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/config/current-context # tanomis config current-context Reference for tanomis config current-context: print the active context name. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis config current-context --help for the version you have installed. ## Usage ```text title="Usage" tanomis config current-context ``` ## Description Print the active context name.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/config/delete-context # tanomis config delete-context Reference for tanomis config delete-context: remove a context and clear its cached credentials. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis config delete-context --help for the version you have installed. ## Usage ```text title="Usage" tanomis config delete-context NAME ``` ## Description Remove a context from the config file and clear its cached credentials. If NAME is the active context, the current-context is unset and you are prompted to select a new one with 'tanomis config use-context'.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/config/use-context # tanomis config use-context Reference for tanomis config use-context: set the active context. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis config use-context --help for the version you have installed. ## Usage ```text title="Usage" tanomis config use-context NAME ``` ## Description Set the active context.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/config/view # tanomis config view Reference for tanomis config view: show the effective configuration. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis config view --help for the version you have installed. ## Usage ```text title="Usage" tanomis config view ``` ## Description Show the effective configuration.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/docs # tanomis docs Reference for tanomis docs: serve the documentation to coding agents. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis docs --help for the version you have installed. ## Usage ```text title="Usage" tanomis docs ``` ## Description Serve the documentation to coding agents. ## Commands | Command | Description | |---|---| | [`tanomis docs serve`](/cli/docs/serve) | serve the documentation to a coding agent as MCP tools over stdio |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/docs/serve # tanomis docs serve Reference for tanomis docs serve: serve the documentation to a coding agent as MCP tools over stdio. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis docs serve --help for the version you have installed. ## Usage ```text title="Usage" tanomis docs serve [flags] ``` ## Description Runs a Model Context Protocol server on standard input and output with three tools: search_docs, read_doc, and list_docs. The documentation is the public site as one file, fetched on first use and cached; it is revalidated when the copy is older than an hour and served from the cache when the site cannot be reached. No credential is used. Register it with `skills install`, or point your agent at this command directly. ## Flags | Flag | Type | Description | |---|---|---| | `--cache-dir` | string | Directory for the cached copy (default: docs-cache beside the CLI config file). | | `--docs-url` | string | Documentation site to serve. Default `https://docs.tanomis.com`. | | `--refresh` | bool | Ask the site for a fresh copy on first use even if the cache is recent. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/login # tanomis login Reference for tanomis login: log in to a hosted control plane via OIDC. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis login --help for the version you have installed. ## Usage ```text title="Usage" tanomis login [flags] ``` ## Description Authenticate an operator against a hosted control plane and cache a refreshable token. Defaults to the device authorization grant; use `--loopback` for the browser authorization-code flow (requires an IdP app that allows it). With `--console`, bootstrap the context from the control plane's GET /v1/cli-config first. ## Flags | Flag | Type | Description | |---|---|---| | `--ca-bundle` | string | Extra CA bundle (PEM) to trust for the IdP. | | `--console` | string | Control-plane console/runtime base URL to bootstrap from. | | `--device` | bool | Use the device authorization grant (default). | | `--loopback` | bool | Use the browser authorization-code + PKCE loopback flow (requires an IdP app that allows it). | | `--name` | string | Context name to create/update (default: console host). | | `--tenant` | string | Preferred tenant/org (reserved; passthrough). |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--verbose` | bool | Verbose output. |
## Examples --- Source: /cli/logout # tanomis logout Reference for tanomis logout: clear cached credentials for the active (or --context) context. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis logout --help for the version you have installed. ## Usage ```text title="Usage" tanomis logout ``` ## Description Clear cached credentials for the active (or `--context`) context.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/m2m # tanomis m2m Reference for tanomis m2m: manage organization machine clients (human owner/admin login required). Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis m2m --help for the version you have installed. ## Usage ```text title="Usage" tanomis m2m ``` ## Description Manage organization machine clients (human owner/admin login required). ## Commands | Command | Description | |---|---| | [`tanomis m2m create`](/cli/m2m/create) | create a client and show its secret once | | [`tanomis m2m delete`](/cli/m2m/delete) | delete a client and its credentials | | [`tanomis m2m disable`](/cli/m2m/disable) | disable a client at the next authorization decision | | [`tanomis m2m enable`](/cli/m2m/enable) | re-enable a client | | [`tanomis m2m get`](/cli/m2m/get) | inspect a client and its credential metadata | | [`tanomis m2m ls`](/cli/m2m/ls) | list machine clients | | [`tanomis m2m permissions`](/cli/m2m/permissions) | change a client's permission profile or scopes | | [`tanomis m2m rename`](/cli/m2m/rename) | rename a client | | [`tanomis m2m revoke`](/cli/m2m/revoke) | revoke a credential | | [`tanomis m2m rotate`](/cli/m2m/rotate) | create an overlapping credential and show its secret once | ## Flags | Flag | Type | Description | |---|---|---| | `--org` | string | Organization ID or slug (default: your only organization). |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/m2m/create # tanomis m2m create Reference for tanomis m2m create: create a client and show its secret once. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis m2m create --help for the version you have installed. ## Usage ```text title="Usage" tanomis m2m create NAME [flags] ``` ## Description Create a client and show its secret once. ## Flags | Flag | Type | Description | |---|---|---| | `--expires-in-days` | int | Credential lifetime (1–365 days). Default `90`. | | `--profile` | string | Permission profile: observe, invoke, deploy. Default `observe`. | | `--request-id` | string | Stable request ID for safe retries; secrets cannot be replayed. | | `--scopes` | stringSlice | Custom comma-separated scopes. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/m2m/delete # tanomis m2m delete Reference for tanomis m2m delete: delete a client and its credentials. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis m2m delete --help for the version you have installed. ## Usage ```text title="Usage" tanomis m2m delete CLIENT ``` ## Description Delete a client and its credentials.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/m2m/disable # tanomis m2m disable Reference for tanomis m2m disable: disable a client at the next authorization decision. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis m2m disable --help for the version you have installed. ## Usage ```text title="Usage" tanomis m2m disable CLIENT ``` ## Description Disable a client at the next authorization decision.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/m2m/enable # tanomis m2m enable Reference for tanomis m2m enable: re-enable a client. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis m2m enable --help for the version you have installed. ## Usage ```text title="Usage" tanomis m2m enable CLIENT ``` ## Description Re-enable a client.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/m2m/get # tanomis m2m get Reference for tanomis m2m get: inspect a client and its credential metadata. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis m2m get --help for the version you have installed. ## Usage ```text title="Usage" tanomis m2m get CLIENT ``` ## Description Inspect a client and its credential metadata.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/m2m/ls # tanomis m2m ls Reference for tanomis m2m ls: list machine clients. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis m2m ls --help for the version you have installed. ## Usage ```text title="Usage" tanomis m2m ls ``` ## Description List machine clients. Aliases: `list`.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/m2m/permissions # tanomis m2m permissions Reference for tanomis m2m permissions: change a client's permission profile or scopes. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis m2m permissions --help for the version you have installed. ## Usage ```text title="Usage" tanomis m2m permissions CLIENT [flags] ``` ## Description Change a client's permission profile or scopes. ## Flags | Flag | Type | Description | |---|---|---| | `--profile` | string | Permission profile: observe, invoke, deploy. Default `observe`. | | `--scopes` | stringSlice | Custom comma-separated scopes. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/m2m/rename # tanomis m2m rename Reference for tanomis m2m rename: rename a client. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis m2m rename --help for the version you have installed. ## Usage ```text title="Usage" tanomis m2m rename CLIENT NAME ``` ## Description Rename a client.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/m2m/revoke # tanomis m2m revoke Reference for tanomis m2m revoke: revoke a credential. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis m2m revoke --help for the version you have installed. ## Usage ```text title="Usage" tanomis m2m revoke CLIENT CREDENTIAL ``` ## Description Revoke a credential.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/m2m/rotate # tanomis m2m rotate Reference for tanomis m2m rotate: create an overlapping credential and show its secret once. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis m2m rotate --help for the version you have installed. ## Usage ```text title="Usage" tanomis m2m rotate CLIENT [flags] ``` ## Description Create an overlapping credential and show its secret once. ## Flags | Flag | Type | Description | |---|---|---| | `--expires-in-days` | int | Credential lifetime (1–365 days). Default `90`. | | `--label` | string | Credential label. Default `rotation`. | | `--request-id` | string | Stable request ID for safe retries; secrets cannot be replayed. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/mcp # tanomis mcp Reference for tanomis mcp: manage governed MCP connectors. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis mcp --help for the version you have installed. ## Usage ```text title="Usage" tanomis mcp ``` ## Description Manage governed MCP connectors. ## Commands | Command | Description | |---|---| | [`tanomis mcp connector`](/cli/mcp/connector) | create, inspect, and test MCP connectors |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/mcp/connector # tanomis mcp connector Reference for tanomis mcp connector: create, inspect, and test MCP connectors. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis mcp connector --help for the version you have installed. ## Usage ```text title="Usage" tanomis mcp connector ``` ## Description Create, inspect, and test MCP connectors. ## Commands | Command | Description | |---|---| | [`tanomis mcp connector create`](/cli/mcp/connector/create) | create an MCP connector | | [`tanomis mcp connector discover`](/cli/mcp/connector/discover) | discover tools and print the inventory diff | | [`tanomis mcp connector get`](/cli/mcp/connector/get) | show one MCP connector | | [`tanomis mcp connector list`](/cli/mcp/connector/list) | list MCP connectors | | [`tanomis mcp connector review`](/cli/mcp/connector/review) | set review status for one discovered MCP tool | | [`tanomis mcp connector set-status`](/cli/mcp/connector/set-status) | set a connector's lifecycle status (draft\|active\|degraded\|disabled\|retired) | | [`tanomis mcp connector test`](/cli/mcp/connector/test) | probe connector reachability and tools/list | | [`tanomis mcp connector tools`](/cli/mcp/connector/tools) | list discovered tools for an MCP connector |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/mcp/connector/create # tanomis mcp connector create Reference for tanomis mcp connector create: create an MCP connector. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis mcp connector create --help for the version you have installed. ## Usage ```text title="Usage" tanomis mcp connector create [flags] ``` ## Description Create an MCP connector. ## Flags | Flag | Type | Description | |---|---|---| | `--auth-mode` | string | Backend auth mode. Default `none`. | | `--by` | string | Identity recorded as creator. Default `local-dev`. | | `--ca-bundle-ref` | string | Trust bundle reference. | | `--credential-ref` | string | Connector credential reference. | | `--kind` | string | Connector kind. Default `remote_http`. | | `--name` | string | Connector name. | | `--network-mode` | string | Network mode. Default `public`. | | `--scope` | string | Scope: tenant or global. Default `tenant`. | | `--status` | string | Connector status. Default `draft`. | | `--target-url` | string | Absolute http(s) MCP endpoint URL. | | `--tenant-id` | string | Tenant id for tenant-scoped connectors. | | `--transport` | string | MCP transport. Default `streamable_http`. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/mcp/connector/discover # tanomis mcp connector discover Reference for tanomis mcp connector discover: discover tools and print the inventory diff. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis mcp connector discover --help for the version you have installed. ## Usage ```text title="Usage" tanomis mcp connector discover CONNECTOR_ID ``` ## Description Discover tools and print the inventory diff.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/mcp/connector/get # tanomis mcp connector get Reference for tanomis mcp connector get: show one MCP connector. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis mcp connector get --help for the version you have installed. ## Usage ```text title="Usage" tanomis mcp connector get CONNECTOR_ID ``` ## Description Show one MCP connector.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/mcp/connector/list # tanomis mcp connector list Reference for tanomis mcp connector list: list MCP connectors. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis mcp connector list --help for the version you have installed. ## Usage ```text title="Usage" tanomis mcp connector list [flags] ``` ## Description List MCP connectors. ## Flags | Flag | Type | Description | |---|---|---| | `--tenant-id` | string | Tenant id used to include tenant-scoped connectors. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/mcp/connector/review # tanomis mcp connector review Reference for tanomis mcp connector review: set review status for one discovered MCP tool. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis mcp connector review --help for the version you have installed. ## Usage ```text title="Usage" tanomis mcp connector review CONNECTOR_ID TOOL_NAME [flags] ``` ## Description Set review status for one discovered MCP tool. ## Flags | Flag | Type | Description | |---|---|---| | `--by` | string | Identity recorded as reviewer. Default `local-dev`. | | `--status` | string | Review status: approved, rejected, or pending. Default `approved`. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/mcp/connector/set-status # tanomis mcp connector set-status Reference for tanomis mcp connector set-status: set a connector's lifecycle status (draft|active|degraded|disabled|retired). Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis mcp connector set-status --help for the version you have installed. ## Usage ```text title="Usage" tanomis mcp connector set-status CONNECTOR_ID STATUS [flags] ``` ## Description Set a connector's lifecycle status through the registry's validated transition route. The server is the only authorization opinion (manage:mcp-connectors or the armed workspace capability) and enforces the transition rules; the CLI only refuses values outside the vocabulary so a typo fails before a network call. The primary use is activating a delegated connector: probe and discover never change a per-user connector's status, so activation is an explicit operator action rather than a side effect of a health check. disabled and retired take the connector away from every member and, where the gateway provisioner is armed, tear down its data-plane routes — so both require `--yes`, like the other verbs whose mistakes are felt by someone else. ## Flags | Flag | Type | Description | |---|---|---| | `--yes` | bool | Confirm a disabled/retired transition without prompting. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/mcp/connector/test # tanomis mcp connector test Reference for tanomis mcp connector test: probe connector reachability and tools/list. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis mcp connector test --help for the version you have installed. ## Usage ```text title="Usage" tanomis mcp connector test CONNECTOR_ID ``` ## Description Probe connector reachability and tools/list.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/mcp/connector/tools # tanomis mcp connector tools Reference for tanomis mcp connector tools: list discovered tools for an MCP connector. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis mcp connector tools --help for the version you have installed. ## Usage ```text title="Usage" tanomis mcp connector tools CONNECTOR_ID ``` ## Description List discovered tools for an MCP connector.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/org/tenants # tanomis org tenants Reference for tanomis org tenants: provision and manage your organization's tenants. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis org tenants --help for the version you have installed. ## Usage ```text title="Usage" tanomis org tenants ``` ## Description Provision and manage your organization's tenants. ## Commands | Command | Description | |---|---| | [`tanomis org tenants create`](/cli/org/tenants/create) | provision a tenant in this organization | | [`tanomis org tenants get`](/cli/org/tenants/get) | inspect a tenant | | [`tanomis org tenants ls`](/cli/org/tenants/ls) | list organization tenants | | [`tanomis org tenants offboard`](/cli/org/tenants/offboard) | withdraw access and tear down a tenant's resources | | [`tanomis org tenants retry`](/cli/org/tenants/retry) | retry failed provisioning | ## Flags | Flag | Type | Description | |---|---|---| | `--org` | string | Organization ID or slug (default: your only organization). |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/org/tenants/create # tanomis org tenants create Reference for tanomis org tenants create: provision a tenant in this organization. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis org tenants create --help for the version you have installed. ## Usage ```text title="Usage" tanomis org tenants create NAME [flags] ``` ## Description Provision a tenant in this organization. ## Flags | Flag | Type | Description | |---|---|---| | `--mode` | string | Saas\|enterprise-byo\|enterprise-managed. Default `saas`. | | `--request-id` | string | Stable request ID to safely retry tenant creation. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/org/tenants/get # tanomis org tenants get Reference for tanomis org tenants get: inspect a tenant. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis org tenants get --help for the version you have installed. ## Usage ```text title="Usage" tanomis org tenants get TENANT ``` ## Description Inspect a tenant.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/org/tenants/ls # tanomis org tenants ls Reference for tanomis org tenants ls: list organization tenants. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis org tenants ls --help for the version you have installed. ## Usage ```text title="Usage" tanomis org tenants ls ``` ## Description List organization tenants. Aliases: `list`.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/org/tenants/offboard # tanomis org tenants offboard Reference for tanomis org tenants offboard: withdraw access and tear down a tenant's resources. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis org tenants offboard --help for the version you have installed. ## Usage ```text title="Usage" tanomis org tenants offboard TENANT [flags] ``` ## Description Withdraw access and tear down a tenant's resources. ## Flags | Flag | Type | Description | |---|---|---| | `--confirm` | string | Repeat the tenant ID to confirm resource teardown. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/org/tenants/retry # tanomis org tenants retry Reference for tanomis org tenants retry: retry failed provisioning. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis org tenants retry --help for the version you have installed. ## Usage ```text title="Usage" tanomis org tenants retry TENANT ``` ## Description Retry failed provisioning.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/session # tanomis session Reference for tanomis session: run, observe, stop, and reap runtime sessions. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis session --help for the version you have installed. ## Usage ```text title="Usage" tanomis session ``` ## Description Run, observe, stop, and reap runtime sessions. ## Commands | Command | Description | |---|---| | [`tanomis session file`](/cli/session/file) | manage files in an existing session | | [`tanomis session get`](/cli/session/get) | show one session | | [`tanomis session ls`](/cli/session/ls) | list sessions | | [`tanomis session run`](/cli/session/run) | invoke an agent: create a session, start a run, and stream its output | | [`tanomis session stop`](/cli/session/stop) | stop a running session |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/session/file # tanomis session file Reference for tanomis session file: manage files in an existing session. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis session file --help for the version you have installed. ## Usage ```text title="Usage" tanomis session file ``` ## Description Manage files in an existing session. ## Commands | Command | Description | |---|---| | [`tanomis session file get`](/cli/session/file/get) | download a session file | | [`tanomis session file ls`](/cli/session/file/ls) | list session files | | [`tanomis session file put`](/cli/session/file/put) | upload a file without starting a run | | [`tanomis session file rm`](/cli/session/file/rm) | delete a session file |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/session/file/get # tanomis session file get Reference for tanomis session file get: download a session file. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis session file get --help for the version you have installed. ## Usage ```text title="Usage" tanomis session file get SESSION FILE OUTPUT ``` ## Description Download a session file.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/session/file/ls # tanomis session file ls Reference for tanomis session file ls: list session files. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis session file ls --help for the version you have installed. ## Usage ```text title="Usage" tanomis session file ls SESSION ``` ## Description List session files. Aliases: `list`.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/session/file/put # tanomis session file put Reference for tanomis session file put: upload a file without starting a run. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis session file put --help for the version you have installed. ## Usage ```text title="Usage" tanomis session file put SESSION PATH [flags] ``` ## Description Upload a file without starting a run. ## Flags | Flag | Type | Description | |---|---|---| | `--media-type` | string | Override media type (otherwise inferred from extension). | | `--upload-key` | string | Idempotency key for prepare retries. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/session/file/rm # tanomis session file rm Reference for tanomis session file rm: delete a session file. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis session file rm --help for the version you have installed. ## Usage ```text title="Usage" tanomis session file rm SESSION FILE ``` ## Description Delete a session file. Aliases: `remove`.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/session/get # tanomis session get Reference for tanomis session get: show one session. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis session get --help for the version you have installed. ## Usage ```text title="Usage" tanomis session get ID ``` ## Description Show one session.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/session/ls # tanomis session ls Reference for tanomis session ls: list sessions. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis session ls --help for the version you have installed. ## Usage ```text title="Usage" tanomis session ls [flags] ``` ## Description List sessions. Aliases: `list`. ## Flags | Flag | Type | Description | |---|---|---| | `--agent` | string | Filter by agent name. | | `--all-users` | bool | Include sessions from all users. | | `--limit` | int | Maximum number of sessions. | | `--status` | string | Filter by status. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/session/run # tanomis session run Reference for tanomis session run: invoke an agent: create a session, start a run, and stream its output. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis session run --help for the version you have installed. ## Usage ```text title="Usage" tanomis session run AGENT [flags] ``` ## Description Invoke an agent from the CLI. Creates a session for AGENT in the caller's tenant, starts a run, and streams the run's events to stdout, the same runtime path the Console drives (no shortcut). Useful for development and for producing runtime evidence (tool / egress ledger entries). Bound by the caller's token and tenant scope; grants no privilege the Console would not. ## Flags | Flag | Type | Description | |---|---|---| | `--contract-ref` | string | What this work is judged against, set when the work is opened and never afterwards. | | `--contract-version` | string | The revision of the contract named by `--contract-ref` (required with it). | | `--file` | stringSlice | Upload and attach a local file (repeatable). | | `--idempotency-key` | string | Replay the original run on retry instead of starting a new one. | | `--input` | string | Raw JSON input object (mutually exclusive with `--prompt`). | | `--model` | string | Model for the new session (bounded by the agent's permitted_models when model.invoke enforces). | | `--no-stream` | bool | Start the run and print its id without streaming events. | | `--no-wait` | bool | When a rule holds the run for approval, print the hold and exit instead of waiting for the answer. | | `--objective` | string | What this work is for, in your words, shown to whoever reviews it (set when the work is opened). | | `--prompt` | string | Prompt string sent as \{"prompt": ...\}. | | `--session` | string | Post the run to an existing session instead of creating one (needed to release a hold a person approved on it). | | `--work-id` | string | Continue an existing unit of work instead of opening a new one (mutually exclusive with `--work-subject`). | | `--work-subject` | string | Declare the subject of the work this run serves (settlement-mode agents refuse to run without work). |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
## Examples --- Source: /cli/session/stop # tanomis session stop Reference for tanomis session stop: stop a running session. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis session stop --help for the version you have installed. ## Usage ```text title="Usage" tanomis session stop ID ``` ## Description Stop a running session.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/skills # tanomis skills Reference for tanomis skills: install the platform skills for coding agents (Claude Code, Codex, Cursor). Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis skills --help for the version you have installed. ## Usage ```text title="Usage" tanomis skills ``` ## Description Skills teach a coding agent how the platform works and how to drive this CLI: how to build an agent, deploy and roll it out, run and debug it, and govern it. They are embedded in this binary and rendered from its own command tree, so re-run install after upgrading. ## Commands | Command | Description | |---|---| | [`tanomis skills install`](/cli/skills/install) | write the platform skills into a coding agent's skills directory | | [`tanomis skills list`](/cli/skills/list) | list the platform skills embedded in this binary | | [`tanomis skills show`](/cli/skills/show) | print one rendered skill without installing it |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/skills/install # tanomis skills install Reference for tanomis skills install: write the platform skills into a coding agent's skills directory. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis skills install --help for the version you have installed. ## Usage ```text title="Usage" tanomis skills install [flags] ``` ## Description Writes one SKILL.md per skill under the agent's skills directory: repo scope writes at the root of the current repository (.claude/skills, .agents/skills, .cursor/skills); user scope writes under your home directory. Re-running is safe: a file this CLI generated and nobody edited is updated in place; a file it did not generate, or one edited since install, is left alone unless `--force`. Do not edit installed skills: copy one under another name instead, so upgrades keep refreshing the original. ## Flags | Flag | Type | Description | |---|---|---| | `--agent` | string | Coding agent(s) to install for: all, or a comma-separated list of claude, codex, cursor. Default `all`. | | `--dir` | string | Explicit skills directory to write into (overrides `--agent` and `--scope`). | | `--force` | bool | Overwrite skill files this CLI did not generate. | | `--no-mcp` | bool | Do not register the documentation server (docs serve) with the agent. | | `--scope` | string | Where to install: repo (the current repository) or user (your home directory). Default `repo`. |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/skills/list # tanomis skills list Reference for tanomis skills list: list the platform skills embedded in this binary. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis skills list --help for the version you have installed. ## Usage ```text title="Usage" tanomis skills list ``` ## Description List the platform skills embedded in this binary.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/skills/show # tanomis skills show Reference for tanomis skills show: print one rendered skill without installing it. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis skills show --help for the version you have installed. ## Usage ```text title="Usage" tanomis skills show SKILL ``` ## Description Print one rendered skill without installing it.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/version # tanomis version Reference for tanomis version: print tanomis version information. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis version --help for the version you have installed. ## Usage ```text title="Usage" tanomis version ``` ## Description Print tanomis version information.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/workspace # tanomis workspace Reference for tanomis workspace: discover and select a managed-SaaS workspace. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis workspace --help for the version you have installed. ## Usage ```text title="Usage" tanomis workspace ``` ## Description Discover and select a managed-SaaS workspace. Aliases: `ws`. ## Commands | Command | Description | |---|---| | [`tanomis workspace clear`](/cli/workspace/clear) | clear the active context's workspace selection | | [`tanomis workspace ls`](/cli/workspace/ls) | list the organizations and tenants you can access | | [`tanomis workspace show`](/cli/workspace/show) | show the workspace selected on the active context | | [`tanomis workspace use`](/cli/workspace/use) | select a workspace and persist it on the active context |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/workspace/clear # tanomis workspace clear Reference for tanomis workspace clear: clear the active context's workspace selection. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis workspace clear --help for the version you have installed. ## Usage ```text title="Usage" tanomis workspace clear ``` ## Description Clear the active context's workspace selection.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/workspace/ls # tanomis workspace ls Reference for tanomis workspace ls: list the organizations and tenants you can access. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis workspace ls --help for the version you have installed. ## Usage ```text title="Usage" tanomis workspace ls ``` ## Description List the organizations and tenants you can access. Aliases: `list`.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/workspace/show # tanomis workspace show Reference for tanomis workspace show: show the workspace selected on the active context. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis workspace show --help for the version you have installed. ## Usage ```text title="Usage" tanomis workspace show ``` ## Description Show the workspace selected on the active context.
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--tenant` | string | Tenant id (X-Tenant-ID). | | `--verbose` | bool | Verbose output. |
--- Source: /cli/workspace/use # tanomis workspace use Reference for tanomis workspace use: select a workspace and persist it on the active context. Generated from the tanomis command tree on 2026-10-01. Flags and defaults match the binary; run tanomis workspace use --help for the version you have installed. ## Usage ```text title="Usage" tanomis workspace use ORG_SLUG [--tenant TENANT_ID] [flags] ``` ## Description Select one of your granted workspaces. The selection is validated against GET /v1/me/workspaces (the only authority for what you may pick) and stored on the active context. With `--tenant` omitted, the organization's default tenant is used when there is exactly one obvious choice. ## Flags | Flag | Type | Description | |---|---|---| | `--tenant` | string | Tenant id to select within the organization (default: the org's default tenant). |
Global flags | Flag | Type | Description | |---|---|---| | `--config` | string | Config file path. | | `--context` | string | Config context to use. | | `-o`, `--output` | string | Output format: table\|json. | | `--verbose` | bool | Verbose output. |
--- Source: /concepts/agents-and-versions # Agents and versions An agent is a name; a version is an immutable build plus its declared contract; enablement decides which tenants a version serves. An agent on Tanomis is not a container you run. It is a name that owns a lineage of immutable versions, and a set of per-tenant decisions about which version serves each tenant's traffic. - **Agent**: a stable name, such as `support-copilot`. - **Version**: one immutable build of that agent, with the manifest contract captured at registration. Change anything and you publish a new version. - **Enablement**: a tenant-scoped assignment that says "this tenant serves this version". No version receives traffic until a tenant is enabled for it. - **Warm start**: a version's sessions start from a pre-built snapshot when one exists, so a new session is ready in well under a second instead of booting the harness cold. ## A version is immutable `tanomis agent deploy` packages your source deterministically, builds it remotely, and registers the result as a version with everything the manifest declared: entry point, harness, models, network egress, policy ceiling, connectors, resources. That contract is validated at registration and frozen for the version's lifetime. This is what makes rollouts safe. A version that served traffic yesterday will behave the same way today, and rolling back means enabling a version that already exists rather than rebuilding anything. ## Version status and enablement are separate A version has a **status**: it starts as a draft, and it must be at production status before a tenant can serve it. Promotion changes the global status only. It routes no traffic on its own. A tenant has an **enablement** for the agent: which version it serves as the baseline, and optionally which version it serves as a canary at a percentage share. Enablement is the commit point, and it is always tenant-scoped. ```bash title="Terminal" tanomis agent versions support-copilot --by-tenant # who serves which version tanomis agent rollout support-copilot --version v2 --tenants acme-prod --canary-share 10 ``` `rollout` does both steps for the tenants you name: it promotes the version if needed, then enables it for exactly those tenants, as their baseline or as a canary. Other tenants are never touched. See [Roll out and roll back](/guides/deploy/rollout-and-rollback) for the full procedure. `tanomis agent promote` moves a version to production status and nothing else. If you want a tenant to serve it, enable it for that tenant, or use `rollout`, which does both. ## Warm starts For a deployed version, Tanomis builds a golden snapshot: a checkpoint of the agent's warmed-up process taken over an empty workspace, containing no tenant data. When a session starts, the snapshot is restored and the session's own workspace is mounted into it, so the first turn does not wait for the harness to boot. You do not manage snapshots. The deploy builds or reuses one before the tenant is enabled, so the first session never races the build. A version whose snapshot could not be produced still deploys; its sessions start cold, and the deploy output says so. Idle sessions are suspended the same way and restored when work arrives. An idle session costs storage, not a running process, which is why sessions can be long-lived. See [Sessions and runs](/concepts/sessions-and-runs). ## Models and the LLM gateway A version declares the model it uses and, optionally, the models a run may request: ```yaml title="agent.yaml" model: "your-workspace-model-id" permitted_models: - "your-workspace-model-id" ``` Every model call from an agent goes through the tenant's LLM gateway. That is where model access is authorized, where usage is metered per run, and where model traffic is traced. An empty `permitted_models` does not widen access: the decision falls to platform and tenant policy. A run that requests a model outside the allowlist is refused. ## What a version carries | Declared in `agent.yaml` | What it governs | Where it is enforced | |---|---|---| | `entrypoint`, `framework` | How the harness starts your code | Build and runtime | | `model`, `permitted_models` | Which models a run may call | LLM gateway | | `network.egress` | Which external hosts the agent may reach | Deploy (grammar and tenant ceiling), runtime proxy | | `policy.approval_required_tools`, `policy.enable_ask_user` | Which actions pause for a person | Runtime, as durable interactions | | `policy.scope_ceiling` | The most authority the agent's own tokens can carry | Delegation and token exchange | | `integrations` | Which governed tools the agent may call | Gateway, per tool | | `delegation` | Whether the agent calls or accepts work from other agents | Control plane, per call | | `session.idle_timeout`, `session.max_lifetime` | How long a session lives | Session lifecycle | The complete field list, defaults, and validation rules are in the [Agent manifest reference](/reference/agent-manifest). ## Where to go next - [Deploy a version](/guides/deploy/deploy-a-version): what happens between `tanomis agent deploy` and an enabled tenant. - [Roll out and roll back](/guides/deploy/rollout-and-rollback): canaries, graduation, and rollback. - [Policy, approvals, and evidence](/concepts/governance): the controls a version's contract participates in. --- Source: /concepts/governance # Policy, approvals, and evidence The controls that decide what an agent may do, the points where a person can step in, and the records that prove what happened. Tanomis's promise is that an agent can act, and every action stays attached to who asked, what was allowed, and what actually happened. This page is the map of those controls. Each one has its own guide; this is where they fit together. - **Declared ceilings** in `agent.yaml` bound what a version may reach: models, external hosts, connector tools, and the authority its own tokens can carry. - **Tenant ceilings** set by an administrator only ever narrow a declaration. - **Approvals** pause a run for a person at the actions you choose, and survive disconnects. - **Two evidence planes** record it: a tamper-evident audit ledger for decisions and attribution, and an execution trace for what the agent did, kept apart on purpose. ## Every control is a ceiling, and ceilings only narrow A version declares what it needs. An administrator may set a per-tenant ceiling. The effective permission is the intersection, and no layer can add to what a lower one declared. | Control | Declared by the author | Bounded by the tenant | Enforced at | |---|---|---|---| | Models | `model`, `permitted_models` | Tenant model policy | The LLM gateway, per call | | External hosts | `network.egress` | Tenant egress ceiling | Deploy time, then the runtime proxy per connection | | Connector tools | `integrations..tools.allow` | Compatible connection and agent access | The gateway, per tool call | | Agent authority | `policy.scope_ceiling` | The caller's own ceiling on a delegated call | Delegation and token exchange | Where a declaration exceeds the tenant ceiling, the deploy fails and names both the entry and the ceiling that excluded it, before anything is built. You find out in seconds. ## Approvals: a person decides at the moments you choose Two manifest settings turn specific moments into durable interactions: - **`policy.approval_required_tools`** lists the harness tools that must wait for a permission decision. Each call emits a permission request; the run pauses until a person allows or denies. - **`policy.enable_ask_user`** lets the agent ask a person a question mid-run and wait for the answer. Governed connector tools can be marked the same way with `approval_required_tools` on the integration grant. An interaction is a control-plane record, not a live socket. It survives a closed browser tab, a restarted application, or a suspended session. A decision is accepted exactly once, so two reviewers cannot both answer; the second sees the recorded answer. The application flow is in [Approval-required actions](/guides/run/approval-required-actions); the Console renders the same requests as allow/deny cards. ## Identity: who asked, and who acted Every request carries two identities kept apart: the **invoking principal** (the person or service that started the work, resolved from the access token and the selected workspace) and the **agent actor** (the version that acted, attested by the platform with a per-run identity that the agent process cannot forge). When one agent hands work to another, the callee's effective authority is what it was granted for that call, intersected with both agents' ceilings. Authority narrows at every hop and never widens. See [Identity and access](/concepts/identity-and-access) and [Delegate work to another agent](/guides/run/delegate-to-another-agent). ## Evidence: two records, kept apart | Plane | What it records | Properties | Who uses it | |---|---|---|---| | Audit ledger | Attribution, policy decisions, approvals, delegation decisions, enablement changes, content hashes | Tenant-isolated, append-only, hash-chained, signed. It carries references and decisions, never prompt or tool payload bodies. | Auditors, compliance, incident review | | Execution trace | One trace per run: model calls, tool calls, timing, and content captured at the level tenant policy allows | Mutable evaluation data with its own capture, sampling, and retention policy | Engineers debugging behaviour, evaluators | They share the run and session identifiers, so a ledger decision can point at the trace content it was made about, by hash, without copying the content into the ledger. Read the organization's trail with `tanomis org audit`; see [Audit and evidence](/guides/audit/audit-and-evidence). A valid ledger chain proves the platform's record is intact and unaltered. It does not by itself prove that an external system changed the way the agent intended. For effects on other systems, verify in that system. ## What every run gets The causal order is **identity → context → policy checkpoint → agent action → optional human decision → evidence**. - **Identity on both sides.** The person or service that asked, and the exact agent version that acted, attested by the platform. - **A policy checkpoint before the action.** Models, hosts, connector tools, and delegated authority are ceilings that only narrow. - **A person when you want one.** Approval requests and questions pause the run and survive disconnects; the first answer wins. - **Evidence you can hand to an auditor.** A signed, append-only ledger of decisions, and a separate execution trace of what the agent did. > **Availability:** Shared connectors are configured per tenant; durable approvals are on in hosted Tanomis. See [What is available where](/reference/availability). ## The journey, with the controls in place 1. **Build**: declare models, hosts, connectors, approvals, and the authority ceiling in `agent.yaml`. 2. **Deploy**: the declaration is validated against the tenant's ceilings before a build starts, then frozen into the version. 3. **Run**: the gateway, the egress proxy, and the connector gateway enforce per call; approvals pause the run where you asked them to. 4. **Review**: the ledger holds the decisions; the trace holds the execution. ## Where to go next - [Restrict network egress](/guides/govern/restrict-network-egress) - [Approval-required actions](/guides/run/approval-required-actions) - [MCP connectors](/guides/connect-tools/mcp-connectors) - [Audit and evidence](/guides/audit/audit-and-evidence) --- Source: /concepts/identity-and-access # Identity and access Who asked, who acted, and how a request is resolved. The identity postures an agent can declare, what the platform actually establishes today, and why a declaration never grants access. Identity answers **who is acting**. Access answers **whether that actor may perform this action now**. Tanomis keeps the two apart: an identity mode declares the posture an agent intends, the authorization path narrows what any credential may carry, and the target resource makes the final decision. - Two identities on every request: the **invoking principal** (person or service, from the token) and the **agent actor** (the version, attested by the platform). - An agent's **identity mode** declares its posture; its **scope ceiling** is the most authority its own tokens can ever carry. - Authority only narrows: brokers intersect, they never widen. Delegated authority is what was granted for the call, not the agent's registered envelope. - The evidence records both identities, so an auditor can see who asked and which version acted. > **Availability:** Service-mode bind-time identity is enabled per tenant by a platform operator; where it is off, the session runs without an identity assertion. Scoped and delegated authority are held contracts: a public runtime workflow for scoped service API authority is not currently exposed. See [What is available where](/reference/availability). Service (conditional): the stable agent machine principal can receive a per-bind identity assertion with no resource or scopes; it is not run-bound, and scoped service API authority has no supported public runtime path. Delegated (held): the contract intends a user subject plus agent actor and an on-behalf-of credential, but no generally supported tenant-bound, agent-attributed production exchange exists yet. Hybrid (conditional): bind-time identity is conditional, while both scoped authority paths are held; one credential path is intended per acquisition and authority is never combined. If a future supported flow requests scoped authority, the contract model applies an effective ceiling and any supplied target audience. Approval classification is delegated-only, has no supported completion path, and autonomous acquisition skips it. The authorization contract can remain cached for up to 30 seconds. Declaring an identity mode, scope, audience, model, or tool is never a grant. Each declaration is a ceiling, posture, or request that a later enforced decision can narrow or refuse. ## How a request is resolved Four things turn a request into an authorized action, in this order: **CLI context → authenticated principal → selected workspace projection → organization + tenant**. 1. A **CLI context** is local configuration: a named target environment plus its authentication state. It is not an organization or tenant. 2. The credentials identify the **authenticated principal**: the user, service, or agent whose authority bounds the request. 3. Workspace discovery returns only the projections granted to that principal. A **selected workspace** combines one organization, one attached tenant, the caller's role, and effective capabilities. 4. The **organization and tenant** in that projection scope the resources the request may touch. Selecting a workspace supplies request context; it does not create a resource or widen the caller's grants. The API's `X-Organization-ID` and `X-Workspace-Tenant-ID` headers select one granted projection; despite its compatibility name, `X-Workspace-Tenant-ID` carries the selected tenant ID. See [Platform API request basics](/api#request-basics). ## Two identities on every run The **invoking principal** starts the request: a signed-in person, a backend with an [automation client](/guides/deploy/automation-credentials), or another agent. The selected workspace supplies the organization and tenant that principal may use. The **agent actor** is the version that does the work. Where service-mode identity is enabled, the session bind also establishes a stable machine identity for the agent process. The session records the immutable execution version regardless. | Question | Answer today | |---|---| | Who selected the organization and tenant? | The invoking principal and its selected workspace. | | Which code is running? | The immutable version and tenant enablement bound to the session. | | Which identity can be established at bind? | The stable agent machine principal. Its assertion has no resource or scopes and is not proof of one run. | | Which ceiling would a scoped request use? | The agent's broker-cached authorization contract, not the session's execution version. No public runtime flow makes that request today. | | Was the final action allowed? | The target resource's own authorization result, never the manifest or token alone. | Delegated and hybrid modes describe an intended model in which a user remains the subject and the agent is verifiably attributed as the actor. Do not describe a delegated token or audit entry as a verified on-behalf-of chain until a supported flow says it is. ## Identity modes Hosted manifests must declare one mode: ```yaml identity_mode: service inherits_user_permissions: false ``` | Mode | Intended authority | Availability | |---|---|---| | `service` | The agent's own machine principal. | Bind-time identity is enabled per tenant and can degrade to a session without an assertion. The runtime requests no scoped API authority. | | `delegated` | A user subject with the agent verifiably attributed as actor. | Contract-defined; the tenant-bound, agent-attributed exchange is held. Autonomous acquisition is rejected. | | `hybrid` | Service or delegated authority, one credential path per acquisition. | Bind-time identity is conditional; both scoped authority paths are held. The two authorities are never unioned. | `identity_mode` is pinned by the agent's first non-rejected version and must stay the same across later versions; a rejected first attempt does not pin it. A different trust model is a different agent. `inherits_user_permissions: true` is valid only with `delegated` or `hybrid`, and opts the version into an impersonation approval gate before it can become serveable; passing that gate does not make the held delegated flow available. ## What the service assertion is, and is not When enabled and established at session bind, service-mode identity asks for no target resource and no scopes. The result is an **identity assertion**, not a scoped API credential. The runtime injects it into the agent process and records the returned stable fingerprint on session-start evidence; a warm process can reuse the assertion until it expires, so it does not prove which run presented it. A record without the fingerprint means no identity was established for it; historical records are not backfilled. The manifest still declares `policy.scope_ceiling` and `policy.allowed_audiences` as the agent's authorization contract. A future supported flow would fit requested scopes to that ceiling and check a target audience. Today, the shipped runtime request supplies neither, no public workflow accepts a scoped service-token request, and autonomous acquisition does not evaluate approval sensitivity rules. The intended delegated design adds that effective scopes also fit the user's entitlement, one acquisition uses one user-backed credential path, and the credential and evidence preserve the user subject and verified agent actor. The hosted exchange currently defers user-entitlement intersection to identity-provider issuance, so requested scopes are not proof of effective scopes; the complete path is held, and its approval-required response has no durable approval ID, decision endpoint, or resume path. Durable runtime [questions and tool decisions](/guides/run/approval-required-actions) are a separate, supported mechanism. Do not infer scoped API authority from a service identity token, call an internal token endpoint, or build an approval UI from these concepts. Keep final action authorization at the target resource. ## Changing a version's authorization A version's manifest is immutable; to change `scope_ceiling`, `allowed_audiences`, or approval thresholds, register a new version. The scoped-authorization contract has its own clock: where the internal broker exercises it, it reads the agent's effective registered manifest through a 30-second in-process cache, not the session's execution version. A registration can change that contract before the new version receives traffic, and rolling execution traffic back does not necessarily restore the earlier contract. Review authorization impact at registration time and verify the effective contract after a change; use [Roll out and roll back](/guides/deploy/rollout-and-rollback) to move traffic, not as an authorization rollback. ## Three controls, three questions | Control | What it bounds | |---|---| | `policy.scope_ceiling` | Concrete authority a minted agent token may carry. | | `permitted_models` | Which model a run may select. | | Integration `tools.allow` | Which published-contract tools the agent requests; current workspace access is checked separately. | Adding an `mcp:...` scope does not approve a connector tool, and adding an `llm:...` scope does not replace the model allowlist. See the [connector authority gates](/guides/connect-tools/mcp-connectors#read-the-four-independent-gates). ## When it stops | When | Typical reason | What to do | |---|---|---| | Validate or deploy | Unknown mode, wildcard grant, missing audience for a non-empty ceiling, or `inherits_user_permissions` with service mode. | Correct the manifest before publishing. | | Register a later version | Its `identity_mode` differs from the pinned mode. | Keep the mode, or create a separate agent. | | Establish bind-time identity | Identity is disabled, fails softly, or is denied in enforce mode. | An absent fingerprint is the visible no-identity result; confirm tenant configuration with your platform operator. | | Authorize the final action | The target resource rejects the token or operation. | Inspect that resource's policy outcome. A declaration cannot override it. | | Design scoped or delegated API authority | You need a public scoped token flow, tenant-bound delegation, or approval completion. | The public path is held. Wait for a supported integration guide. | Treat an infrastructure error separately from a policy denial, and never widen the manifest to get past a transient failure. ## Reading attribution The Console's agent identity view shows the stable non-secret fingerprint, pinned mode, provisioning state, ceiling, and allowed audiences. It never shows the identity provider's principal reference or a client secret. Session and ledger evidence identify the agent and version and include the fingerprint when an identity was established. Session views describe the recorded subject plus the configured posture; they do not prove a fully persisted delegation chain. The `tanomis session run` command intentionally prints session and run identifiers and agent output, not machine-identity fields. Never copy a provider client ID, secret, or internal token into application logs. ## Backend and CI identity An [automation client](/guides/deploy/automation-credentials) identifies the backend or pipeline making a platform request. It is separate from the identity an agent uses while running. Owners and admins choose its permissions; the platform checks its live state and tenant membership on every decision, so disabling the client or removing a scope applies without waiting for a token to expire. ## Where to go next - [Agent manifest](/reference/agent-manifest#attribution-and-identity): the identity and policy fields. - [Sessions and runs](/concepts/sessions-and-runs): the durable session and each admitted run. - [Policy, approvals, and evidence](/concepts/governance): where identity sits among the controls. --- Source: /concepts/organizations-and-tenants # Organizations and tenants How a customer organization, its members and roles, its runtime tenants, and the workspace you select fit together. Your organization is your account. Your tenants are where agents run. A workspace is one organization plus one of its tenants, as seen by you. Everything you do with `tanomis`, the Console, or the Platform API happens inside one workspace. - An **organization** owns members, roles, invitations, and one or more tenants. - A **tenant** is the runtime and data-isolation boundary: agents, versions, sessions, and files live in exactly one tenant and never cross it. - A **workspace** is your view of one organization and one attached tenant, with the role and capabilities you hold there. You select it once; the server re-checks it on every request. ## The three objects | Object | What it holds | What it is not | |---|---|---| | Organization | Members, roles, invitations, tenant attachments, the audit trail | A runtime boundary. Nothing executes "in" an organization. | | Tenant | Enabled agents and versions, sessions, runs, files, connectors, policy ceilings | A customer account. A tenant belongs to exactly one organization. | | Workspace | The pair you selected plus your effective role and capabilities | A third resource. It is a projection of the other two for one caller. | An organization owns one tenant per customer, and usually a sandbox tenant that serves no customer. Adding a tenant later does not change identities, credentials, or API contracts. One attachment can be marked as the organization's default. ## Roles and capabilities Every member has one role in the organization. Roles map to a small, fixed set of capabilities. | Role | Reach | Capabilities | |---|---|---| | Owner | Every tenant in the organization | `manage`, `deploy`, `run`, `view` | | Admin | Every tenant in the organization | `manage`, `deploy`, `run`, `view` | | Developer | Only tenants named in an explicit grant | As granted, from `deploy`, `run`, `view` | | Viewer | Only tenants named in an explicit grant | As granted, usually `view` | What the capabilities mean in practice: - **`view`** inspects agents, versions, sessions, runs, and files in the tenant. - **`run`** creates sessions and runs enabled agents. - **`deploy`** deploys and changes agent versions and their enablement. - **`manage`** governs connector configuration where that is separately enabled. It does not grant organization or tenant administration; membership and grants are owner and admin work. A grant names a member and a tenant. Accepting an invitation automatically grants the organization's default tenant; if the organization has no default, the new member needs a grant before they can select a workspace. ## How access is decided Access is never something a client asserts. On every request the server resolves: 1. **Who you are** from the access token. 2. **Which organization** you asked for, and that you are a member of it. 3. **Which tenant** you asked for, that it is attached to that organization, and that your role or a grant reaches it. 4. **Whether the operation** is within your capabilities there. Naming an organization or tenant in a header or a CLI flag can only narrow what your token already allows. It cannot widen it, and it cannot reach a tenant you are not granted. Memberships and grants can change at any time; the server re-resolves them when each request arrives, so a revoked grant takes effect on the next call. ## Selecting a workspace Discovery returns only what you may select: ```bash title="Terminal" tanomis workspace ls tanomis workspace use northwind --tenant bluewater-bank tanomis workspace show ``` `workspace ls` lists your organizations and the tenants each grants you, with each tenant's state. Select a tenant whose state is `ready`; a tenant that is still being provisioned can be saved as a selection, but the server refuses tenant-scoped operations until it becomes ready. Applications do the same discovery over HTTP with [List workspaces](/api/workspaces/list-workspaces), then send the organization and tenant as request headers. See [Invoke from your application](/guides/run/invoke-from-your-application). ## Organization automation An organization also owns its [automation clients](/guides/deploy/automation-credentials). Each client reaches all active, ready tenants attached to that organization, including future tenants. Its permission profile restricts actions across that reach. Human tenant grants and machine permissions are separate: machines do not inherit a human workspace selection or use per-tenant grants. A signed-in owner or admin manages their lifecycle. ## Where to go next - Invite people and grant tenants: [Organization members and access](/guides/manage/organization-members). - Understand how agents and versions live inside a tenant: [Agents and versions](/concepts/agents-and-versions). - Sign in and select a workspace from the CLI: [Authenticate and select a workspace](/getting-started/authenticate-and-select-workspace). --- Source: /concepts/platform-model # Platform model The six nouns that explain Tanomis: platform, organization, tenant, agent version, session, run. How they nest, who works at each level, and what an operation changes. Six nouns explain everything in Tanomis: **platform**, **organization**, **tenant**, **agent version**, **session**, **run**. You are the organization; your customers are the tenants. Here is how they nest, who works at each level, and what an operation changes. ## How things nest Your company is one **organization**. Each of your customers is a **tenant**: their own isolated runtime, with its own policy, connectors, sessions, and data, served by the agents you enable for them. An agent you deploy once can serve every customer, each on the version you choose. The **platform** underneath is Tanomis's in the hosted service and yours in a self-hosted install; either way, it hosts your organization and runs the shared infrastructure. Need somewhere to test? Keep an internal tenant that serves no customer; a canary to one customer works the same way. Inside every tenant the runtime chain is the same: **agent → version → enablement → session → run**. An agent is a stable name. A version is an immutable build of it. An enablement says which version this tenant serves. A session is a conversation or unit of work against that version, and a run is one request inside the session. Files, approvals, and questions attach to the session; the audit ledger and trace record every run. ## Who works where | Role | Level | What they do | Tool | |---|---|---|---| | Platform operator | Platform | Installs and upgrades Tanomis, provisions tenants, deploys global agents, sets tenant ceilings for models, egress, and connectors. Tanomis staff in the hosted service; your own team when self-hosted. | `tanomis`, Console | | Organization owner or admin | Organization | Invites members, sets roles, grants tenants, creates automation credentials, reads the audit trail. | Console, `tanomis` | | Developer | Tenant | Writes agents, deploys versions, canaries a new version to a few customers and rolls back, evaluates. | `tanomis`, CI | | Your application | Tenant | Creates sessions and starts runs on behalf of a customer, in that customer's tenant; streams events, answers approvals, attaches files. | Platform API | | Reviewer or auditor | Tenant | Answers approvals in the Work queue, reads runs, exports evidence packages. | Console | ## The six nouns | Noun | What it is | Owned by | Changes when | |---|---|---|---| | Platform | One Tanomis deployment: shared infrastructure, global agents, and the operators who run it. Hosted by Tanomis, or installed by you. | Platform operators | You install or upgrade it. | | Organization | Your company on Tanomis: its members, roles, tenant grants, and automation credentials. One per customer. Nothing runs here. | Your owners and admins | A member, role, or grant changes. | | Tenant | One of your customers: an isolated runtime with the agents it serves, its policy ceilings, its connectors, and its data. Nothing is shared between tenants. | Your organization | Policy or enablement changes; offboarding destroys it. | | Agent version | An immutable build of a named agent plus its declared contract. An **enablement** says which version a tenant serves. | Whoever deployed it | Never. A change is a new version; enablement moves traffic. | | Session | A conversation or unit of work against one version in one tenant, with its workspace, files, and approvals. Suspended when idle. | Your application, or the person who opened it | Each run adds to it; it expires by idle or lifetime limit. | | Run | One request inside a session, streamed as events, ending in one authoritative status. Every run leaves a ledger entry and a trace. | The session | Only its status, until it is terminal. | ## What changes what | You do | It creates or changes | Scope | |---|---|---| | `tanomis agent deploy` | A new immutable version, its warm-start snapshot, and an enablement. | The tenants named, or all with `--scope global --tenants all` | | `tanomis agent promote` | The version's global status only. No traffic moves. | Platform | | `tanomis agent enable` or `rollout` | Which version a tenant serves, as baseline or canary share. | The tenants named | | Change tenant policy | Ceilings for models, hosts, connectors, spend, for one customer. Applies to their next run. | One tenant | | Grant or revoke a member | Who may see or act in a tenant. Takes effect on the next request. | Organization | | Offboard a tenant | When a customer leaves: destroys their sessions, workspaces, and enablements. Ledger exports survive. | One tenant, confirmed by name | ## Where to go next - [Organizations and tenants](/concepts/organizations-and-tenants): roles, grants, and how access is decided. - [Agents and versions](/concepts/agents-and-versions): immutability, enablement, warm starts. - [Sessions and runs](/concepts/sessions-and-runs): the execution lifecycle. - [Identity and access](/concepts/identity-and-access): who asked, who acted, and how the API resolves a request. --- Source: /concepts/security-model # Security and isolation model How agents are isolated from each other and from the platform, how tenants are kept apart, and what is signed. An agent runs untrusted code on behalf of your tenant. Tanomis's job is to let it act while keeping it inside a boundary: isolated from other agents, unable to reach anything you did not declare, and unable to forge its own identity or its own record. - Each session runs in its own sandbox with a kernel-level isolation layer, on workers that are interchangeable and hold no session identity. - Tenants never share a gateway, a workspace, or a snapshot's data. - Everything an agent reaches goes through a platform-controlled path: the LLM gateway, the connector gateway, the egress proxy, the delegation broker. - Images and golden snapshots are signed; run identity is a platform-signed assertion custodied outside the agent process. ## Isolation | Boundary | How it is kept | |---|---| | Between sessions | A nested sandbox per session with a kernel-level isolation layer. The worker pod that hosts it is generic: session identity lives in the control plane, the workspace, and the snapshot, never in the worker. | | Between tenants | Per-tenant worker pools, per-tenant LLM gateway, per-tenant trace and ledger partitions, tenant-scoped storage. A request that names another tenant is refused at the front door. | | Between an agent and the platform | The agent sees `/workspace` and the platform's own endpoints. Every other destination is refused unless declared and inside the tenant ceiling. | A golden snapshot, the warm-process checkpoint sessions start from, is taken over an empty workspace and contains no tenant data. The session's own workspace is mounted into it at restore. ## Every path out is governed | Path | Control | |---|---| | Model calls | The tenant's LLM gateway: allowlist, metering, tracing | | Tool calls | The connector gateway: manifest-declared connector and tools, current agent access, connection and account readiness, exact-call approvals | | External hosts | A per-connection egress proxy: declared hosts within the tenant ceiling, public addresses only | | Other agents | The delegation broker: a live grant, narrowed authority, a recorded decision | There is no direct network route from an agent to the internet or to the cluster it runs in. ## Identity that cannot be forged The invoking principal comes from the access token; the agent actor is attested by the platform with a per-run assertion custodied outside the agent process. Anything the agent process can read authenticates nothing on its own. See [Identity and access](/concepts/identity-and-access). ## Supply chain Agent images are built remotely from a deterministic bundle and registered by digest. Golden snapshots are signed platform artifacts. A version's contract is captured at registration and immutable after it. What ran is therefore what was reviewed. ## Records that cannot be quietly edited The audit ledger is append-only, hash-chained, and signed per tenant; the execution trace is kept apart under its own capture and retention policy. See [Audit and evidence](/guides/audit/audit-and-evidence). ## Where to go next - [Policy, approvals, and evidence](/concepts/governance) - [Restrict network egress](/guides/govern/restrict-network-egress) - [Data handling and retention](/guides/govern/data-handling): planned page on storage and retention. --- Source: /concepts/sessions-and-runs # Sessions and runs A session is where state lives; a run is one request inside it. Statuses, one-active-run, idempotent retries, and what to do when a run waits for a person. A **session** is the continuity boundary for one enabled agent version: its workspace, files, and interactions. A **run** is one admitted unit of execution inside it. Keep both IDs: the session tells you where state lives; the run tells you what happened during one request. - Create a **session** once per conversation or unit of work; start a **run** per request. Runs in a session execute one at a time. - Send an `idempotency_key` on both so a retry returns the original instead of duplicating work. - The **run resource** is the authoritative status. The event stream is how you watch it; reconnect with `Last-Event-ID`. - A run can pause for a person (`awaiting_input: true`); a session can be stopped and resumed, and idle sessions are suspended for you. Session `ready` → Run 1 `running` → interaction `pending` (`awaiting_input: true`) → Run 1 `succeeded` → session `idle` → Run 2 `running` → Run 2 `failed` → session `idle`. Run 2 starts only after Run 1 is terminal; the failed second run does not erase the first or end the session. ## Choose the invocation boundary | Choose | When it fits | Tradeoff | |---|---|---| | [One-shot invocation](/api/agents/invoke-agent) | One independent request where immediate streaming is the main goal. | Tanomis creates a session and run and returns their IDs in `X-Session-ID` and `X-Run-ID`, but retrying the request creates a new session boundary. | | [Explicit session](/api/sessions/create-session) | Retries, multiple runs, durable interaction, files, or reconnecting to events matter. | Two requests to begin: create the session, then start the run. | One-shot still creates durable resources; record the IDs it returns. For anything your product depends on, create the session explicitly. ## Read resource state Session status describes whether the continuity boundary can accept or continue work: | Session status | Meaning | |---|---| | `creating` | Tanomis is provisioning the session. | | `ready` | The session is prepared and can accept a run. | | `running` | A run is active. | | `idle` | No run is active; the session can be reused. | | `stopping` | Stop is fencing new work and releasing the active runtime. | | `stopped` | The session was explicitly stopped and must be resumed before another run. | | `failed` | Session activation or runtime failed; explicit resume can attempt recovery. | | `expired` | The session reached its lifecycle limit; explicit resume can reactivate it. | Run status describes one execution attempt: | Run status | Meaning | |---|---| | `queued` | Admitted and waiting for dispatch. | | `running` | Executing or waiting on a durable human interaction. | | `succeeded` | Completed successfully. | | `failed` | Ended with an error; inspect `error_code` and `error_message` when present. | | `cancelled` | Ended after cancellation. | `succeeded`, `failed`, and `cancelled` are terminal. A terminal run stays readable through the [run resource](/api/runs/start-run), and the session returns to `idle` for the next run. The public session status does not reveal worker residency. An `idle` session may keep a runtime warm or be transparently suspended and restored when work arrives. Do not call resume for that; reserve it for `stopped`, `failed`, or `expired` sessions. ## One run at a time One session admits at most one active run, where active means `queued` or `running`. Starting another run while one is active returns `409 Conflict`. Wait for a terminal state, use another session for independent concurrency, or retry the same run idempotently. Terminal runs do not hold the slot, so a reused session is an ordered history: ```text session A run 1: succeeded run 2: failed run 3: running ``` Files and durable interactions belong to the session. Attachments select session files for one run; they do not move the files anywhere. ## Make retries deliberate Use a stable idempotency key for one logical operation and never recycle it for new intent. Keys are opaque, at most 255 bytes, and scoped differently at each boundary: | Operation | Same key | Changed intent or no key | |---|---|---| | Create session | For the same authenticated caller, tenant, and provisioning inputs, returns the original non-terminal session with `200 OK`. | Changed provisioning inputs return `409 Conflict`. Reusing the key after the original session is `stopped`, `failed`, or `expired` returns `412 Precondition Failed`. Omitting the key creates a new session. | | Start run | Within the same session, returns the original active or terminal run; a JSON replay is `200 OK`, while a streaming replay follows the original event stream. | Changed input, model, or work does not replace the original run, so bind the key to one immutable request in your client. Attachment-set drift returns `409 Conflict`. A new or absent key starts a new run only when no run is active; otherwise it returns `409 Conflict`. | The run key is scoped to the session; the same string in another session refers to nothing. A one-shot retry creates another session first, so a run key in its body cannot make the whole one-shot operation retry-safe. ## Inspect, cancel, stop, and resume Resource reads are authoritative. A stream close, command exit, or accepted cancellation does not replace them. | Action | Request | Then | |---|---|---| | Read | `GET /v1/sessions/{session_id}` and `GET /v1/sessions/{session_id}/runs/{run_id}` | Use the run's status; the session read carries `awaiting_input`. | | Cancel | `POST /v1/sessions/{session_id}/runs/{run_id}/cancel` | Read the run until it is terminal. The session's history is untouched. | | Stop | `POST /v1/sessions/{session_id}/stop` | Stop fences new runs and settles active work before `stopped`. A conflict during admission means the boundary is still settling: inspect and retry. | | Resume | `POST /v1/sessions/{session_id}/resume` | Resume only a `stopped`, `failed`, or `expired` session. It keeps the session ID and pinned version and returns to `ready`; on a live session it conflicts. | ## Handle a run waiting for a person > **Availability:** Durable interactions are on in hosted Tanomis and operator-enabled when self-hosted. See [What is available where](/reference/availability). A pending interaction adds no run status. The run remains `running`; a session read exposes the derived signal `awaiting_input: true`. List the session's pending interactions, answer by the stable `interaction_key`, and keep observing the same run. Tanomis may suspend and restore the runtime while it waits without changing the public model. The full first-answer-wins flow is in [Approval-required actions](/guides/run/approval-required-actions). A run may instead bind to durable settlement work that can continue from a later session; see [Durable work and settlement](/guides/run/durable-work-and-settlement). ## Keep lineage separate A later run continues the same session. It is not a fork and it is not a delegated session; those create separate lineage and authority and have their own guides. ## Where to go next - [Invoke from your application](/guides/run/invoke-from-your-application): the six-step flow from a backend. - [Stream and replay events](/guides/run/stream-and-replay-events): watching a run safely. - [Session workspace and state](/guides/build/session-workspace-and-state): what persists. --- Source: /getting-started/authenticate-and-select-workspace # Authenticate and select a workspace Sign in to hosted Tanomis as a person, choose a CLI context and granted workspace, and keep that human login out of application backends. Sign in as yourself, pick one workspace you are granted, and check the result before you deploy or run anything. Most people do this once per machine. - `tanomis login` is a browser device flow for a person; the CLI stores the login in a named **context**. - A **workspace** is one organization and one tenant you are granted. `tanomis workspace use` selects it; `tanomis workspace show` proves it. - The server re-checks membership and grants on every request. Selecting never widens access. - Pipelines and backends use an automation client, never this login. > **Backend boundary:** This login belongs to the person using the CLI. Do not read the CLI > credential store from application code, copy its refreshable credentials into a service, or use > platform-service or agent machine credentials. Pipelines and backends use an organization-owned > automation client; see > [Automation credentials](/guides/deploy/automation-credentials). The complete path is: ```text Hosted API -> named CLI context -> signed-in person -> granted workspace -> tenant-scoped request ``` The first three choices are local request context. The server still verifies the credential, organization membership, tenant grant, and required operation on every request. ## 1. Sign in to a named context ```bash tanomis login --console https://api.tanomis.com --name saas ``` `tanomis` reads the public environment configuration from the hosted API, so it discovers the API origin and OIDC settings rather than relying on copied values. Device authorization is the hosted default: the CLI prints a verification address and a short code, you sign in and approve in a browser as yourself, and the CLI stores short-lived access and refreshable login state for the `saas` context. A device flow can start on a headless terminal, but it is not unattended: a person must finish the browser step. Do not automate it as a service credential. ```bash tanomis auth status tanomis config current-context ``` `auth status` shows the context, authentication mode, API target, login state, and token expiry, never the token itself. A normal result names `saas` and reports `logged-in: yes`. Tokens are short-lived; `tanomis` refreshes them before a command, and if the refresh grant is gone it stops and asks you to `tanomis login` again. ## 2. Understand context versus workspace A context chooses an environment and its local authentication configuration. A workspace chooses one organization and attached tenant from the projections granted to the signed-in person. | Object | What it selects | What it cannot do | |---|---|---| | CLI context | One API environment and its login state | Grant organization or tenant access | | Authenticated principal | The person whose authority bounds the request | Borrow another member's role | | Selected workspace | One granted organization, tenant, role, and capability set | Create, attach, or reassign a tenant | | Tenant | The runtime and data-isolation boundary | Expand the caller's credential or membership | Use another configured context as the default, or target it for one command: ```bash tanomis config use-context staging tanomis config current-context tanomis workspace ls --context staging ``` Login and workspace selection are stored per context, so switching context can change the API environment, the cached login, and the selected workspace together. Check both before a consequential command. The current CLI does not display the account identity behind a cached login; to establish it again, log out and log in as the intended account. ## 3. Discover your granted workspaces ```bash tanomis config use-context saas tanomis workspace ls ``` ```text ORGANIZATION DISPLAY NAME ROLE TENANT STATE DEFAULT CAPABILITIES northwind Northwind developer bluewater-bank ready * run,view northwind Northwind developer northwind-sandbox ready deploy,run,view ``` The list contains only organizations you belong to and only tenants granted to you. Capabilities summarize what you may do in each: | Capability | What it allows | |---|---| | `view` | Inspect permitted resources | | `run` | Create sessions and run enabled agents | | `deploy` | Deploy or change agent versions where the command permits it | | `manage` | Govern supported connector configuration when separately enabled; it does not grant organization or tenant administration | Select a tenant whose `STATE` is `ready`. Discovery can include an attachment that is still being provisioned, and the CLI can save that selection, but the server refuses tenant-scoped operations until the attachment becomes ready. The table is discovery, not a permanent authorization snapshot. Memberships and grants can change, and the server re-resolves them when each request arrives. `workspace use` also saves the displayed capabilities as a local advisory snapshot, so deploy and run commands may deny early when it lacks their capability. After an owner changes a grant, repeat `workspace use` and `workspace show`. ## 4. Select and verify one projection ```bash tanomis workspace use northwind --tenant northwind-sandbox tanomis workspace show ``` ```text context: saas organization: northwind tenant: northwind-sandbox role: developer capabilities: deploy, run, view ``` If the organization has one tenant, or exactly one default, you may omit `--tenant`. With several tenants and no single default, the CLI refuses to guess and asks for `--tenant`. It also rejects an organization or tenant that was not returned by discovery. The selected tenant becomes the command's effective tenant, so omit a redundant `--tenant` flag. Deploy and session-run commands refuse an explicit tenant that differs from the selected workspace; do not assume every other command has that local conflict check. ## 5. Use the workspace, then clean up deliberately ```bash tanomis agent ls ``` For a first deploy and managed run, follow the [Quickstart](/getting-started/quickstart). Its commands name a tenant explicitly; when a workspace is selected, an explicit `--tenant` must exactly match the tenant from `workspace show`, and you can simply omit it. ```bash tanomis workspace clear tanomis logout ``` `workspace clear` removes the local organization and tenant choice from the context and nothing on the server. `logout` clears the cached login and keeps the context; authenticated commands stop until you log in again. Deleting a context is a separate action that also clears its credentials. ## Troubleshoot authentication and selection | What you observe | Meaning | Next action | |---|---|---| | `not logged in: run tanomis login` | The context has no usable access or refresh state. | Run `tanomis login --name CONTEXT`, or repeat `--console` to refresh its configuration. | | Refresh fails | The refresh grant is no longer usable or the identity provider is unavailable. | Retry only for a transient outage; otherwise log in again. | | `no workspaces` | Authentication succeeded, but this person has no active organization membership. | Ask an organization owner to confirm membership. | | An organization row shows tenant `(none)` | Membership exists, but this person has no attached tenant they may access. | Ask an owner to confirm the attachment and, for a developer or viewer, the grant. | | The organization has several tenants and no single default | There is no unambiguous tenant choice. | Repeat `tanomis workspace use ORG_SLUG --tenant TENANT_ID` with a tenant from `workspace ls`. | | The selected tenant's state is not `ready` | Its attachment is still provisioning. | Wait for it to be ready, then select and verify again. | | The workspace lacks `deploy`, `run`, `view`, or `manage` | The projection or saved snapshot does not include the operation family. | Ask an owner to grant it, then repeat `workspace use` and `workspace show`; selecting another value cannot widen access. | | HTTP `401` | No valid accepted credential. | Check `tanomis auth status`, then log in again. | | HTTP `403` | The credential is valid, but this principal is not permitted for the selected projection or operation. | Have an owner verify membership, grant, and capability. | | `--tenant` conflicts with the selected workspace | The explicit tenant and saved tenant differ. | Omit `--tenant`, select the intended workspace, or clear the selection. | Application requests consume bearer tokens and workspace headers as described in [Platform API request basics](/api#request-basics), but that reference does not turn this human login into a backend credential. ## Where to go next - [Quickstart](/getting-started/quickstart): deploy and run your first agent. - [Workspaces](/guides/manage/workspaces): switching in the Console and the API. - [Journey commands](/cli): exact CLI syntax. --- Source: /getting-started/coding-agents # Build with a coding agent Teach Claude Code, Codex, or Cursor to scaffold, deploy, run, and govern Tanomis agents with platform skills and a documentation MCP server, installed by one tanomis command. Most Tanomis agents are written with a coding agent beside the developer. `tanomis` meets it halfway: four skills that teach the agent how the platform works and how to drive `tanomis`, and a documentation server that lets it search and read these docs as tools. Both ship inside the `tanomis` binary, so they always match the commands your version has. - One command installs everything: `tanomis skills install`. - Skills go to `.claude/skills`, `.agents/skills`, and `.cursor/skills` at the root of your repository; the documentation server is registered in `.mcp.json` and `.cursor/mcp.json`. - Every skill ends with a command reference rendered from your binary's own help text, so re-run the install after upgrading `tanomis`. - Included in Tanomis v0.2.0 (first shipped as `cog` v0.1.1). `tanomis version` shows which you have, and the [installer](/getting-started/install) upgrades in place. ## Install the skills From the root of the repository that holds your agents: ```bash tanomis skills install ``` ```text title="Output" created .claude/skills/tanomis-build-agent/SKILL.md created .claude/skills/tanomis-deploy-rollout/SKILL.md created .claude/skills/tanomis-govern/SKILL.md created .claude/skills/tanomis-run-debug/SKILL.md created .mcp.json created .agents/skills/tanomis-build-agent/SKILL.md created .agents/skills/tanomis-deploy-rollout/SKILL.md created .agents/skills/tanomis-govern/SKILL.md created .agents/skills/tanomis-run-debug/SKILL.md created .cursor/skills/tanomis-build-agent/SKILL.md created .cursor/skills/tanomis-deploy-rollout/SKILL.md created .cursor/skills/tanomis-govern/SKILL.md created .cursor/skills/tanomis-run-debug/SKILL.md created .cursor/mcp.json To register the documentation server with Codex, add to ~/.codex/config.toml: [mcp_servers.tanomis-docs] command = "tanomis" args = ["docs", "serve"] ``` Commit the files: they are plain Markdown and JSON, and teammates who clone the repository get the skills without running anything. Re-running the command is safe. A skill file that `tanomis` wrote and nobody edited is updated in place; one you edited, or one `tanomis` did not write, is left alone and listed, unless you pass `--force`. | Agent | Repository scope (default) | User scope (`--scope user`) | |---|---|---| | Claude Code | `.claude/skills/` | `~/.claude/skills/` | | Codex | `.agents/skills/` | `~/.codex/skills/` | | Cursor | `.cursor/skills/` | `~/.cursor/skills/` | Pick agents with `--agent claude,codex` (default `all`), or write into any directory with `--dir`. `tanomis skills list` names the skills; `tanomis skills show deploy-rollout` prints one without installing it. The full flags are on [`tanomis skills install`](/cli/skills/install). ## What the skills cover | Skill | What the agent learns to do | |---|---| | `tanomis-build-agent` | Scaffold with `tanomis agent new`, write the entrypoint, declare models, tools, hosts, and limits in `agent.yaml`, run the contract test, dry-run the deploy. | | `tanomis-deploy-rollout` | Dry-run on a branch, deploy to the sandbox tenant, read version status, canary to named customer tenants with `tanomis agent rollout`, roll back, deploy from CI with an automation client. | | `tanomis-run-debug` | Start sessions and runs from the CLI, read events and the trace, tell a policy refusal from a failure in the agent, unblock a run waiting on an approval, attach files. | | `tanomis-govern` | Add approvals to the right actions, narrow egress, set the authorization ceiling, declare connectors and allowed tools, manage agent access, read the audit trail for a run. | Each skill is a workflow with a checkpoint per step, a table of what to do when a step fails, and a command reference generated from `tanomis --help` at install time. Each also states what it will not do: - Never edit tenant policy to make a run pass. - Never widen a ceiling, an egress list, or a permitted-tools list beyond what the task needs, and never without telling you which line changed and why. - Never store a credential, token, or client secret in the repository, in `agent.yaml`, or in a skill file. ## The documentation server `tanomis docs serve` is a Model Context Protocol server over standard input and output with three tools: `search_docs`, `read_doc`, and `list_docs`. It serves this site, fetched as one file on first use and cached under `~/.config/tanomis/docs-cache` (`--cache-dir` moves it); the copy is revalidated when it is older than an hour and served from the cache when the site cannot be reached. It runs locally, needs no credential, and serves nothing this site does not publish. ```bash tanomis docs serve ``` The install registers it as `tanomis-docs` where the agent reads a JSON file, and prints what to add or run where it does not: | Agent | Registration | |---|---| | Claude Code, repository | Merged into `.mcp.json` | | Claude Code, user | `claude mcp add --scope user tanomis-docs -- tanomis docs serve` | | Cursor | Merged into `.cursor/mcp.json` or `~/.cursor/mcp.json` | | Codex | `[mcp_servers.tanomis-docs]` in `~/.codex/config.toml`, printed by the install | Pass `--no-mcp` to skip the registration. Point the server at another copy of the docs with `--docs-url`, or ask the site for a fresh copy with `--refresh`; see [`tanomis docs serve`](/cli/docs/serve). ## Where to go next - [Quickstart](/getting-started/quickstart): the flow the skills automate. - [`tanomis skills`](/cli/skills) and [`tanomis docs`](/cli/docs) in the CLI reference. - [Docs for LLMs](/llms-full.txt): the same file the documentation server indexes. --- Source: /getting-started/console-tour # Tour the Console What each screen of the Tanomis Console is for, from the first session to the audit export, and when to reach for the CLI or the API instead. The Console is the web front door to your workspace. It drives the same runtime path as the CLI and the Platform API, so anything you do there is inspectable from the others. This tour walks the navigation rail from top to bottom. - **Sessions** is where you talk to an agent and answer its questions and approval requests. - **Agents**, **Connectors**, **Control** are where you inspect and change what is deployed. - **Policies**, **Assurance**, **Work** are where you see what was decided and what is waiting for a person. - **Settings** is your organization: members, roles, tenant grants, workspaces, **Machine clients**, and **Tenants**. Owners and admins can [create automation credentials](/guides/deploy/automation-credentials) and [manage tenants](/guides/manage/tenants-and-environments) here. ## Overview The landing screen for the selected workspace: agent activity, what needs attention, the fleet of enabled agents, connected systems, and the enforcement posture, including counts of actions denied by policy and egress refused. Use it to see at a glance whether anything is waiting or failing. ## Sessions A session is a conversation with one enabled agent version. The screen shows the session rail on the left, the run timeline in the middle, and a composer to start the next run. When a run pauses for a person, the approval or question renders inline as a card; answering it resumes the same run. Each session also exposes its evidence, its delegations (work handed to other agents), and its token usage. Forking a session starts a new lineage from a point in the timeline. ## Agents Every agent enabled for the tenant, with its versions, the manifest each version was registered with, and per-tenant enablement. This is where you confirm which version serves this tenant and read the contract it carries: models, egress, policy ceiling, connectors, and trace capture. ## Connectors Configure service connections and connect your own account where required. In **Agent → Integrations**, select a version, choose a compatible connection, and **Allow access** as an administrator. Personal integrations also need each user's own account. Setup preserves a blocked request; **Continue** starts it only when ready. See [MCP connectors](/guides/connect-tools/mcp-connectors). ## Policies What agents on this tenant may do, and what happened when they tried otherwise. **Rules** is the live policy as a list of sentences (who it applies to, what it watches, what a match refuses) with what each did in the last seven days. A rule opens on its own screen with a form and the same rule as code side by side, and "what it would do" replayed over last week beside it. Edits are kept as you type into one pending change; **Review and publish** shows the diff and the impact, and publishes everything together as the next version. **Versions** rolls back or restores. The Observing / Enforcing switch at the top carries the week's number. **Activity** is the feed of decisions in plain words, each naming the rule, with "Why?" and "Make a rule". See [Write tenant policy](/guides/govern/write-tenant-policy). ## Control Operational controls for what is running: enablement per agent, session lifecycle (stop, resume), and the list of live sessions. Use it when you need to stop serving a version or release a runtime without touching the CLI. ## Assurance The sealed ledger, the policy decisions, the controls an auditor tests, the chain's integrity, and the execution traces. The ledger is filterable by period, agent, decision, and plane. An auditor package can be exported from the Integrity tab. See [Audit and evidence](/guides/audit/audit-and-evidence) for what each record proves. ## Supervision **Supervision** is where people step into agent work. Its **Inbox** lists agent asks waiting for a person: every open permission request and question across the sessions in the selected tenant, longest-waiting first, answerable in place. "Mine" shows the sessions you started; "All in tenant" shows everyone's. Any member of the tenant may answer; the first answer wins. Answering here is the same as answering in the session, and opening the inbox never wakes a parked session. ## Work **Work** is the queue of work the platform could not settle on its own, longest-waiting first, with any settlement question a human still owes at the top. ## Evaluations Where a tenant with evaluation enabled sees evaluation runs, their assessments, cost and latency, and the annotated trace each verdict was made about. See [Evaluate a version](/guides/evaluate/evaluate-a-version). ## Settings Your organization: members and roles, invitations, tenant grants, ownership, and the workspaces you can select. See [Organization members and access](/guides/manage/organization-members). ## Console, CLI, or API? | You want to | Use | |---|---| | Try an agent, answer approvals, read a run's evidence | Console | | Author, deploy, roll out, script an operation | `tanomis` | | Call an agent from your product | Platform API | All three share one control plane, so a session started from the API appears in the Console and a rollout from the CLI is visible under Agents. ## Where to go next - [Quickstart](/getting-started/quickstart): deploy and run your first agent. - [Answer approvals in the Console](/guides/run/answer-approvals-in-the-console) - [Read the audit trail](/guides/audit/read-the-audit-trail) --- Source: /getting-started/get-access # Get access How you get into Tanomis: an organization, an invitation, and a first workspace, and what to ask for if you are setting one up. You reach Tanomis through an organization. Either someone invites you to one, or you are setting one up for your company. Both paths end with a workspace you can select and an agent you can run. - **Joining:** accept the invitation, sign in, select the workspace you were granted. - **Setting up:** Tanomis creates the organization and its first tenant with you as owner; you invite your team and grant tenants. - Sign-in is through the hosted identity provider. Enterprise single sign-on is planned. ## Join an organization 1. Open the invitation link from the email, or run `tanomis org accept --token TOKEN`. 2. Sign in. The hosted login is a browser flow; the CLI's device flow prints a code to enter. 3. If the organization has a default tenant, you are granted it on acceptance. Otherwise an owner grants you a tenant. 4. Select the workspace: `tanomis workspace use ORG --tenant TENANT`, or open the Console. From here, [Quickstart](/getting-started/quickstart) if you are building, or [Tour the Console](/getting-started/console-tour) if you are reviewing. ## Set up an organization Creating an organization and provisioning its first tenant are platform operations performed with Tanomis. Ask for: - the organization name and slug; - the first customer tenant, and whether it should be the default; - a sandbox tenant beside it for testing, if you want one from the start; - the owner: you. Once it exists, [invite your team](/guides/manage/organization-members), grant tenants, and deploy. Owners and admins can create additional tenants and offboard them in **Settings → Organization → Tenants**. Attaching an existing tenant and changing the default remain operator operations; see [Customer tenants](/guides/manage/tenants-and-environments). ## Where to go next - [Authenticate and select a workspace](/getting-started/authenticate-and-select-workspace) - [Organizations and tenants](/concepts/organizations-and-tenants) --- Source: /getting-started/install # Install tanomis Install the tanomis CLI on macOS, Linux, or Windows with one command, verify the download, pin a version in CI, and upgrade. The `tanomis` CLI is how you author, deploy, roll out, and inspect agents from a terminal or a pipeline. It is a single static binary with no dependencies. - macOS and Linux: `curl -fsSL https://get.tanomis.com/install.sh | sh` - Windows: `irm https://get.tanomis.com/install.ps1 | iex` - Every release ships with `SHA256SUMS` and a keyless cosign signature; the installer verifies the checksum before it installs anything. - Pin a version in CI with `TANOMIS_VERSION=v0.2.0`. ## macOS and Linux ```bash curl -fsSL https://get.tanomis.com/install.sh | sh ``` The installer detects your operating system and architecture (amd64 or arm64), downloads the release archive and its checksums, verifies the SHA-256, and installs `tanomis` to `/usr/local/bin` when that is writable, otherwise to `~/.local/bin`. It tells you if that directory is not on your `PATH`. ```text title="Output" Installing tanomis v0.2.0 for darwin/arm64 from https://get.tanomis.com/tanomis/v0.2.0 Installed: tanomis v0.2.0 (commit , built ) -> /usr/local/bin/tanomis Next: tanomis login --console https://api.tanomis.com --name saas ``` | Variable | Effect | |---|---| | `TANOMIS_VERSION` | Install this release instead of the latest, for example `v0.2.0`. | | `TANOMIS_INSTALL_DIR` | Install into this directory instead of the default. | | `TANOMIS_ARCH` | Override the detected architecture, `amd64` or `arm64`. | The former `COG_VERSION`, `COG_INSTALL_DIR`, and `COG_ARCH` settings remain fallbacks when their `TANOMIS_*` counterparts are unset. The installer also writes a `tanomis` compatibility shim that prints `cog: deprecated, use tanomis` to stderr and forwards arguments to `tanomis`. An unrelated existing `tanomis` executable is left in place. ## Windows ```powershell irm https://get.tanomis.com/install.ps1 | iex ``` The script installs `tanomis.exe` to `%LOCALAPPDATA%\Programs\tanomis`, adds that directory to your user `PATH`, and verifies the zip's SHA-256 the same way. Open a new terminal afterwards. The same `TANOMIS_VERSION` and `TANOMIS_INSTALL_DIR` variables apply. ## Verify a download by hand Every release directory carries `SHA256SUMS`, signed keyless with cosign by the release workflow. To verify a download yourself: ```bash base=https://get.tanomis.com/tanomis/v0.2.0 curl -fsSLO "$base/SHA256SUMS" curl -fsSLO "$base/SHA256SUMS.sig" curl -fsSLO "$base/SHA256SUMS.pem" cosign verify-blob --signature SHA256SUMS.sig --certificate SHA256SUMS.pem \ --certificate-identity-regexp '^https://github.com/anyfind/agent-platform/' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com SHA256SUMS curl -fsSLO "$base/tanomis_v0.2.0_linux_amd64.tar.gz" sha256sum --check --ignore-missing SHA256SUMS ``` ## Pin a version in CI Installing in a job is the same one-liner with the version pinned, so a release never changes a pipeline under you: ```yaml title=".github/workflows/agent.yml" - run: curl -fsSL https://get.tanomis.com/install.sh | sh env: TANOMIS_VERSION: v0.2.0 TANOMIS_INSTALL_DIR: /usr/local/bin ``` See [Deploy from CI](/guides/deploy/deploy-from-ci) for the rest of the job and [Automation credentials](/guides/deploy/automation-credentials) for its login. ## Upgrade Run the installer again. It replaces the binary in place, and `TANOMIS_VERSION` unset means the latest release. `tanomis version` prints the installed version, commit, and build date. ## Shell completion ```bash tanomis completion zsh > "${fpath[1]}/_tanomis" # zsh tanomis completion bash > /etc/bash_completion.d/tanomis tanomis completion fish > ~/.config/fish/completions/tanomis.fish ``` ## Where to go next - [Get access](/getting-started/get-access): a workspace to sign in to. - [Authenticate and select a workspace](/getting-started/authenticate-and-select-workspace): the first login. - [Quickstart](/getting-started/quickstart): deploy and run your first agent. --- Source: /getting-started/quickstart # Quickstart Scaffold an agent, deploy it once for every tenant on your platform, run it, and open the result in the Console. Deploy your first agent to every tenant Scaffold an agent, deploy it once for every tenant on your platform, run it, and open the result in the Console. About ten minutes, four commands. - `tanomis` [installed](/getting-started/install) and signed in as a platform operator ([Get access](/getting-started/get-access)) - Python 3.12 or newer and [`uv`](https://docs.astral.sh/uv/) on your `PATH` - A directory in your own repository The journey is **Prepare → Scaffold → Validate → Deploy → Run**. ## 1. Check you are signed in Confirm the context, and see the tenants the deploy will reach: ```bash tanomis auth status tanomis tenant ls ``` ```text title="Output" context: saas logged-in: yes expires in 54m TENANT STATE northwind-sandbox active bluewater-bank active orion-retail active helix-health active ``` **Checkpoint: signed in.** You should see `logged-in: yes` and at least one `active` tenant. Your customers are tenants; the sandbox is the one that serves no customer, and it is where you will run the agent in step 5. ## 2. Scaffold a working agent One command gives you a Claude Agent SDK project that already answers. You will make it yours after it runs. ```bash tanomis agent new support-copilot --harness claude-agents-sdk --agents-root ./agents --description "Triage and draft responses to support requests" ``` ```text agents/support-copilot/ ├── agent.py create_agent(input, context) returns (prompt, options) ├── agent.yaml what the agent may do: model, hosts, tools, limits ├── pyproject.toml ├── uv.lock └── tests/test_agent_contract.py ``` Open `agent.py` and change one line, the `system_prompt`, to "Triage support requests: severity, summary, and a concise draft reply." Everything else stays. Then run the contract test: ```bash cd agents/support-copilot uv run python -m unittest tests/test_agent_contract.py ``` **Checkpoint: project ready.** You should see `OK`. The test checks the entrypoint's shape; your prompt logic is plain Python and needs no runtime to import. ## 3. Dry-run the deploy See exactly what would be built and where it would be enabled, without uploading a byte. This is the same command you will put on every pull request. ```bash uv lock --locked tanomis agent deploy . --scope global --tenants all --dry-run ``` ```text title="Output" dry-run: tanomis agent deploy . name: support-copilot version: 0.1.0 scope: global tenants: all (active customer tenants snapshotted by the server) build profile: python-managed lock policy: uv-frozen files: 5 checksum: sha256:9f1c…e2a0 no changes made (dry-run) — no build was created, no bytes were uploaded ``` **Checkpoint: package ready.** You should see "no changes made". Commit `uv.lock`; remote builds only accept locked projects. ## 4. Deploy to every tenant One command builds remotely, registers an immutable global version with the contract you declared, takes a warm-start snapshot, and enables the version for every active tenant. Nothing routes traffic until that last step. ```bash tanomis agent deploy . --scope global --tenants all ``` ```text title="Output (abridged)" build bld_3f9a2c created for support-copilot@0.1.0 (global, tenants all, profile python-managed, lock uv-frozen) upload done build done image sha256:7ab3… register done support-copilot@0.1.0 golden ready sessions will warm-start enable done 12 tenants → 0.1.0 ``` > **Not a platform operator?** Organization members deploy to their own tenants with > `--tenant TENANT` instead of `--scope global --tenants all`. The rest of this page is the same. Confirm what each tenant now serves: ```bash tanomis agent versions support-copilot --by-tenant ``` **Checkpoint: version enabled.** You should see `enable done` with the tenant count, and every tenant on `0.1.0` in the versions table. A tenant created later is enabled with `tanomis agent enable`. A skipped golden is fine: the version still runs, with cold starts. ## 5. Run it Invoke the agent in the sandbox tenant through the same managed path the Console and your applications use. The command opens a session, starts a run, and streams its events. ```bash tanomis session run support-copilot \ --tenant northwind-sandbox \ --prompt "Customer reports that CSV exports stop at 80%. Draft a first response." ``` ```text title="Output" session ses_7d4f9c run run_a12bc3 version support-copilot@0.1.0 run_started 12:04:01 agent_message Thanks for flagging this. Exports stopping at 80% usually means ... run_completed 12:04:09 status=succeeded ``` **Checkpoint: first run verified.** You should see `run_completed` with `status=succeeded`. If the stream closes without it, the run resource in the Console or API is the authority. Console · Sessions · ses_7d4f9c · run_a12bc3 Open the run in the Console. The same events are there, plus who invoked it, the policy decisions the run passed, and the execution trace. This is the record every run gets; see [Tour the Console](/getting-started/console-tour). ## What you just built The lifecycle from the [Overview](/), with every stage now real. 1. **Source** agents/support-copilot in your repository. 2. **Version** support-copilot@0.1.0, global, immutable. 3. **Enablement** Every customer tenant serves 0.1.0. 4. **Session** ses_7d4f9c, warm-started. 5. **Run** run_a12bc3, succeeded. 6. **Evidence** Ledger entry and trace, in the Console. ## Next Make it yours [Write your agent](/guides/build/write-your-agent) Prompts, tools, and input handling in agent.py; what the harness gives you. Give it tools [MCP connectors](/guides/connect-tools/mcp-connectors) Declare a connector and require confirmation for selected tool calls. Ship the next version [Roll out and roll back](/guides/deploy/rollout-and-rollback) Canary 0.2.0 to a few tenants, graduate it, or restore the baseline in one command.
If something goes wrong | Symptom | Next action | |---|---| | Login required or tenant denied | Run `tanomis login`, then `tanomis auth status`. An administrator confirms your grant. | | Lockfile missing or stale | Run `uv lock`, commit `uv.lock`, repeat the dry run. | | Manifest rejected | Restore the generated fields; read [Manifest essentials](/guides/build/manifest-essentials) before widening anything. | | Build fails after a clean dry run | Read the failing phase in the output, fix the dependency or source error, deploy again. | | First run is slow | The golden was skipped; the version cold-starts and is still usable. | | A model or tool action is denied | Compare with `permitted_models`, the approval list, and tenant policy. Refusals are recorded in the run. |
--- Source: /guides/audit/audit-and-evidence # Audit and evidence Read the organization's audit trail, understand what the ledger proves, and find the execution record for a run. Every governed action on Tanomis leaves a record. This guide shows where to read it and what each record can and cannot prove. - The **audit trail** is the organization's tamper-evident record of decisions: who did what, which policy allowed it, which approvals were given, which versions were enabled where. - The **run record** is the authoritative outcome of each run, with its events replayable by id. - The **execution trace** is the detailed record of what a run did, kept separately from the audit trail so evidence and debugging data can have different retention and access. ## Read the audit trail Owners and admins can read the organization's trail from the Console or the CLI: ```bash title="Terminal" tanomis org audit tanomis org audit --limit 200 tanomis org audit --cursor "$NEXT_CURSOR" ``` Entries include membership and grant changes, invitations, enablement changes such as a rollout or a `disable`, approval decisions, and delegation decisions. Each entry names the principal, the tenant where relevant, the action, and the decision. Content such as prompts or tool payloads is never in the trail; where content matters to a decision, the entry references it by hash. ## What the ledger proves The trail is append-only, hash-chained, and signed per tenant. A valid chain demonstrates that the record has not been altered or truncated since it was written. It is privacy-by-reference: an auditor can verify that a decision was made about specific content without the ledger holding that content. The ledger is the platform's own record. It proves what Tanomis decided and recorded, not what happened inside another system afterwards. To prove an external effect, read that system. ## Read a run's record The run resource is the authoritative status of one unit of work, and its events can be replayed by id at any time while history is retained: ```bash title="Terminal" tanomis session get "$SESSION_ID" curl "$TANOMIS_API/v1/sessions/$SESSION_ID/runs/$RUN_ID/events" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" \ -H "Accept: text/event-stream" \ -H "Last-Event-ID: 0" ``` Replay starts after the id you give, so `Last-Event-ID: 0` reads a run from the beginning. Use the run's terminal event and status together: a stream can end without a terminal frame, and the run resource is what decides. ## Find the execution trace Each run emits one execution trace: model calls, tool calls, timing, and content captured at the level the tenant's capture policy allows (a reference, a redacted copy, or full content with approval). The trace shares the run and session ids with the audit trail, so a decision in the trail can be matched to the execution it was made about. Trace access is tenant-scoped and gated by the tenant's policy at read time. Ask your administrator which capture level your tenant runs and where its traces are served. ## Where to go next - [Policy, approvals, and evidence](/concepts/governance): the model behind these records. - [Stream and replay events](/guides/run/stream-and-replay-events): the replay contract in detail. - [Organization members and access](/guides/manage/organization-members): who can read the trail. --- Source: /guides/audit/export-an-auditor-package # Export an auditor package Hand an auditor a self-contained export of the ledger segment they need, with the integrity metadata to check it. An auditor package is an export of a slice of the audit trail with the integrity metadata needed to check it: the entries, their hash chain, and the signatures. It is built for someone outside your organization who needs to verify what happened without access to your workspace. - Export from **Console → Assurance → Integrity → Export auditor package**, scoped by tenant and time range. - The package contains ledger entries and integrity metadata, never prompt or tool payload bodies. - A valid chain proves the record is intact; it does not by itself prove an external system changed. Read that system for effects. ## Export 1. Open Assurance in the Console for the tenant in question. 2. Filter to the agents, decisions, or time range the request covers. 3. Choose Export auditor package. The export runs in the background; the screen reports when it is ready and where to download it. Owners and admins can export. The export itself is an audited action. ## What is inside | Part | Purpose | |---|---| | Ledger entries | The decisions, attributions, approvals, and enablement changes in scope | | Chain metadata | Each entry's hash and its link to the previous entry | | Signatures | The tenant's signing material so the chain can be checked independently | | Content references | Hashes of the content decisions were about, so an auditor can match content you provide separately without the package carrying it | ## What an auditor can conclude The package demonstrates that the entries are complete for the range and unaltered since they were written, and that each decision was attributed to a principal and a version. Independent assurance beyond that depends on the anchor and key custody your environment uses; an offline verifier is planned, see [Verify the ledger independently](/guides/audit/verify-the-ledger). ## Where to go next - [Audit and evidence](/guides/audit/audit-and-evidence) - [Read the audit trail](/guides/audit/read-the-audit-trail) --- Source: /guides/audit/policy-decisions # Policy decisions Where each allow or deny lands, what a decision record contains, and how to trace a refusal back to the ceiling that caused it. Every governed action passes a decision point before its side effect: the gateway for models and connector tools, the egress proxy for external hosts, the control plane for delegation, the runtime for approvals. Each decision is recorded once, and the Console lists them under **Policies → Activity**. - A decision record names the subject, the action, the resource, the verdict, and the policy or ceiling that decided it. - Verdicts are `allow`, `deny`, or `require-approval`. An approval that was asked for is a decision; the person's answer is a second record. - A verdict is not an outcome. Each record also carries the enforcement outcome (`allow`, `would-deny`, `deny`, `configuration-deny`, `error-open`) and the posture it was decided under (`shadow` or `enforce`). A `deny` recorded as `would-deny` under `shadow` stopped nothing: the Console reads it as "Would have blocked · the request ran anyway". - A decision that could not be made, for example because facts were missing, is recorded as such rather than silently allowed. ## Where decisions are made | Action | Decided at | Typical refusal | |---|---|---| | Model call | The LLM gateway | Model outside `permitted_models` or tenant policy | | Connector tool call | The connector gateway | Tool outside the declared allowlist, connection not ready, current grant revoked | | External connection | The egress proxy | Host outside `network.egress` or the tenant ceiling | | Delegation | The control plane | No grant, or scope outside both ceilings | | Approval-required tool | The runtime | Waits for a person; the answer is the decision | ## Read a decision Open **Policies → Activity** in the Console, or open the Decisions tab of Assurance. Each record shows the verdict, the reason, and the identifiers of the policy or ceiling behind it, plus the run and session it belongs to. A refusal in a run's event stream and its decision record share the run id. ## From a refusal to its cause 1. Read the decision's reason and the control it names. 2. If it names the version's own contract, the fix is a new version: widen `permitted_models`, `network.egress`, or the connector allowlist, and deploy. 3. If it names a tenant ceiling or tenant policy, the fix is administrative: the ceiling must change before any version can pass. 4. If it is an approval, nothing is wrong: a person must answer. See [Approval-required actions](/guides/run/approval-required-actions). Do not "fix" a transient platform error by widening a ceiling; a policy denial and an infrastructure failure are different records. ## Where to go next - [Policy, approvals, and evidence](/concepts/governance): the ceilings in one map. - [Read the audit trail](/guides/audit/read-the-audit-trail) --- Source: /guides/audit/read-the-audit-trail # Read the audit trail Find what happened, who did it, and under which authority, from the Console's Assurance section or from tanomis org audit. The audit trail is the organization's record of decisions. Owners and admins read it in the Console under Assurance, or page through it with `tanomis org audit`. Every entry names an actor, an action, the tenant where relevant, and the decision. - **Console → Assurance** filters by agent, decision, and plane, and opens the ledger entry. - **`tanomis org audit`** pages the same trail from a terminal, newest first. - Content is never in the trail: an entry references what a decision was about by hash. - Members without the owner or admin role see an explanation instead of the trail. ## From the CLI ```bash title="Terminal" tanomis org audit tanomis org audit --limit 200 tanomis org audit --cursor "$NEXT_CURSOR" -o json ``` Use `-o json` in scripts; the cursor in the response continues the page. ## What you will find | Family | Examples | |---|---| | Membership | Invitations sent, accepted, revoked; roles changed; ownership transferred | | Access | Agent connector access enabled and disabled | | Deployment | Versions registered, promoted, enabled, disabled, retired | | Runtime decisions | Policy allow and deny verdicts, egress refusals, model refusals | | Approvals | Permission requests, who answered, what they decided | | Delegation | Each `agent.delegate` attempt, admitted or refused, with the authority chain | | Support | Actions taken under a support grant, with the grant | ## Read one entry An entry carries the principal that acted, the agent version where one acted, the run and session ids where the action belongs to a run, the policy and decision, and hashes of any content the decision was about. The run and session ids are how you get from an entry to the execution trace and back; see [Traces and content classes](/guides/audit/traces-and-content-classes). ## From the Console The Ledger tab of Assurance lists entries across the selected tenant with filters for agent, decision, and plane. Selecting an entry opens the ledger record. Decisions shows the policy decisions on their own with a link into the ledger entry for each; see [Policy decisions](/guides/audit/policy-decisions). ## Where to go next - [Audit and evidence](/guides/audit/audit-and-evidence): what the ledger proves. - [Export an auditor package](/guides/audit/export-an-auditor-package): handing the record to someone outside your organization. --- Source: /guides/audit/traces-and-content-classes # Traces and content classes Match a ledger decision to the execution it was made about, and understand which content class you are allowed to see when you do. The ledger says what was decided; the execution trace says what the agent did. They are kept apart on purpose and joined by the run and session identifiers, so an auditor can move from a decision to the execution behind it, at the content level the tenant permits. - Every ledger entry that belongs to a run carries the run and session ids; every trace span carries the same. - Content in a trace is classified: `reference`, `redacted`, or `full`. The class you are served is decided at read time from the tenant's current policy. - A ledger entry references trace content by hash, so a decision can be checked against the content without the ledger holding it. ## From a decision to a trace 1. In Assurance, open the decision and choose its trace link. Traces live in the Quality section's Traces tab, where evals grade them, and the link opens the run's trace there. 2. To browse instead, open Quality, then Traces, and look the run up by session or trace id. 3. The span for the decided action, a tool call or a model call, shows the decision's identifiers beside the captured content. The Sessions screen offers the same path from the other side: a session's evidence panel lists its runs' decisions and links into the trace. ## Content classes | Class | What you see | Who decides | |---|---|---| | `reference` | Structure and hashes: which calls happened, in what order, with what outcome | Tenant capture policy | | `redacted` | Bodies with sensitive spans removed by tenant redaction rules | Tenant capture policy | | `full` | Complete bodies | Tenant capture policy, and an approval to serve | The class is decided inside the sandbox when the content is captured, from the tenant's policy, and rechecked when you read. The server derives the class from what it captured, never from a label the agent wrote, so an agent cannot promote content into a class it is not allowed to be. ## Matching content by hash A ledger entry about content carries the content's hash. If you hold the content, from a `full` trace or from your own system, hash it and compare. This is how the ledger stays free of prompt and payload bodies while still binding decisions to exact content. ## Where to go next - [Capture execution traces](/guides/evaluate/capture-traces): what a trace contains. - [Audit and evidence](/guides/audit/audit-and-evidence) - [Data handling and retention](/guides/govern/data-handling): planned page on storage and retention. --- Source: /guides/audit/verify-the-ledger # Verify the ledger independently Check the audit ledger's hash chain and signatures with an offline verifier you run yourself. The audit ledger is append-only, hash-chained, and signed. An offline verifier that an auditor runs outside Tanomis is planned. This page is planned and not yet written, because the capability it describes is not yet generally available. The structure is here so you can see where it fits; the notice comes off when it ships. ## What this page will cover - Export a ledger segment. - Run the verifier and read its result. - What an anchor proves and what it does not. ## Meanwhile - [Audit and evidence](/guides/audit/audit-and-evidence) explains what the ledger proves today. --- Source: /guides/build/agent-skills # Add skills to your agent Ship a folder of SKILL.md files with your agent so the model loads the right procedure on demand, on the Claude Agent SDK and Deep Agents harnesses. A **skill** is a folder with a `SKILL.md` in it: a name, a description, and instructions for one job. The model reads only the names and descriptions up front, and pulls the full instructions in when a request calls for that job. That is what lets you ship a dozen detailed procedures without spending a dozen procedures' worth of context on every run. Put the folder in your agent, deploy, and the platform delivers it to the harness. - Skills live in `skills/` next to your `agent.yaml`, and ship inside your agent's image. - The platform mirrors them into the session workspace at `.claude/skills/` on every run. - **Claude Agent SDK:** shipping the folder is enough. - **Deep Agents:** pass `backend=` and `skills=` to `create_deep_agent`. - The mirror is platform-managed. The image is the source of truth, and anything written into the mirror at run time is replaced on the next run. ## Write a skill ```text title="Your agent bundle" my-agent/ ├── agent.py ├── agent.yaml ├── pyproject.toml └── skills/ └── release-notes/ └── SKILL.md ``` The directory name is the skill name, and it has to match the `name` in the frontmatter: ```markdown title="skills/release-notes/SKILL.md" --- name: release-notes description: Turn a list of merged pull requests into release notes grouped by change type. Use when asked for release notes, a changelog, or a summary of what shipped. --- # Release notes Turn merged pull requests into notes a reader can skim. ## Steps 1. Group the PRs by type: Added, Changed, Fixed, Removed. Drop anything purely internal unless the user asks for the full list. 2. Write one line per entry, starting with a past-tense verb. Say what changed for the reader, not which files moved. 3. Put breaking changes first, under their own **Breaking** heading. ## Output Markdown, one `##` heading per group. No preamble and no closing summary. ``` `description` is the only thing the model sees before deciding whether to open the skill, so it carries the whole routing decision. Say what the skill does **and when to reach for it**. "Release notes helper" gives the model nothing to match a request against; the description above names the words a user would actually type. Anything else in the folder (reference files, scripts, templates) ships with it, and the model can read those once the skill is open. Names are restricted to letters, digits, `.`, `_` and `-`. A name outside that set fails the run, because skill names are passed through to the harness as tool-permission entries. ## Ship it Nothing to declare in the manifest. The `skills/` directory's existence is the declaration: ```bash tanomis agent deploy . --tenant acme-prod ``` Your agent's code is baked in at `/agent`, and on each run the platform mirrors `/agent/skills/` into the session workspace at `/.claude/skills/`. That copy is what the harness reads, because a run cannot read your image directly: the agent's working directory is the session workspace, and paths outside it are refused. The image is the source of truth. A skill you delete from the image disappears from the mirror on the next run, and anything written into `.claude/skills/` during a run is replaced. The platform compares the mirror against what it wrote, so an edit made by the agent does not survive, by design: skills are instructions the model follows, and a self-modifying skill tree would be a way for injected text to persist across runs. Two bounds are enforced before anything is written: **500 files** and **5 MB** total. Over either one, the run fails and names the cap it hit. It fails rather than truncating on purpose, because a half-present skill tree is the kind of problem that only shows up the first time someone needs the skill. ## Claude Agent SDK Shipping the folder is the whole job. Project scope is on by default, so the CLI discovers the mirror and the model can use the skills on the next run. To narrow which of your shipped skills are available on a given run, set `skills`: ```python title="agent.py" def create_agent(input_payload, context): return ( input_payload.get("prompt", ""), { "system_prompt": "You write release notes.", "skills": ["release-notes"], # or "all" }, ) ``` Options can be a plain dict, which keeps your module importable without the runtime. See [Claude Agent SDK](/guides/build/claude-agent-sdk) for the full authoring shape. `skills` decides what the model is *shown*. An unlisted skill is hidden from the listing and refused by the Skill tool, but its files are still in the workspace and still readable by any tool that can read files. It is a context-management control, not a security boundary. To keep something away from the agent, do not ship it in the image. Leaving `skills` unset is not "skills off" either. Everything in the mirror is discovered. Set it to a list when you want a narrower set. Changing `skills` partway through a session is rejected, because it reshapes the conversation. Start a new session to change the set. ### Frontmatter that triggers an approval prompt Keep your frontmatter to `name` and `description` unless you need more. A `SKILL.md` that declares `allowed-tools`, `disallowed-tools`, `hooks`, or an execution context is treated by the CLI as a skill that needs confirmation, so **every invocation raises a human-in-the-loop approval** instead of running straight through. In an unattended run that surfaces as a denial rather than a pause. If you need one of those fields, either expect the prompt or name the skill explicitly in `skills`, which registers it as pre-allowed. ## Deep Agents You compile your own graph, so the wiring is yours. Pass **both** `backend=` and `skills=` to `create_deep_agent`: ```python title="agent.py" def create_agent(input_payload, context): from deepagents import create_deep_agent from deepagents.backends import FilesystemBackend backend = FilesystemBackend( root_dir=str(context.workspace_root), virtual_mode=True, ) skills_root = context.workspace_root / ".claude" / "skills" return create_deep_agent( model=context.runtime_context.model, tools=[...], backend=backend, skills=["/.claude/skills/"] if skills_root.is_dir() else None, ) ``` `SkillsMiddleware` registers no tools of its own. It lists each skill and tells the model to `read_file` the path it advertises, and that read goes through **the graph's** backend. So building the middleware yourself and passing it through `middleware=` compiles fine, lists your skills, and then leaves the model unable to open any of them. The failure is quiet: the skills appear in the listing and every attempt to load one comes back empty. Passing `skills=` to `create_deep_agent` is what avoids it, because that builds the middleware against the same `backend` the file tools use. `skills` takes backend paths, not filesystem paths, which is why the backend is rooted at the workspace and the source is the path within it. `virtual_mode=True` is what makes `/.claude/skills/...` resolve as a backend path; it is a path-mapping mode rather than a sandbox, so treat it as wiring, not as a containment control. The `is_dir()` guard keeps the agent working when there is no `skills/` directory, during local development or if you remove it later. ## Check that it loaded Every run with skills present emits an `agent_skills_loaded` event naming what was found, so "did my skill actually ship?" is answerable from the run's event stream: ```json { "type": "agent_skills_loaded", "source": "agent_service", "data": { "skills": ["release-notes"], "file_count": 1, "size_bytes": 742, "changed": true, "warnings": [] } } ``` `skills` lists only the directories that can actually produce a skill. A directory with no `SKILL.md`, no frontmatter, no `description`, or a `name` that does not match its directory is left out and reported in `warnings` instead, because a harness would drop it and the event should not claim otherwise. `SKILL.md` is case-sensitive. If the event is missing entirely, the agent shipped without a `skills/` directory. Check that it sits beside `agent.yaml` and that your build did not exclude it. ## Skills, tools, and connectors Skills are instructions, not capabilities. A skill can tell the model *how* to use a tool, but it cannot grant access to one. What the agent may call is decided by `policy`, by the connectors attached to the version, and by the authorization ceiling, none of which a skill can widen. That is worth keeping in mind when you write one: a skill that says "call the GitHub API" does nothing unless a connector supplying that tool is attached. - **This page:** skills you ship *inside* your agent, for the agent's own model to use. - [`tanomis skills install`](/getting-started/coding-agents): skills installed into *your* coding agent (Claude Code, Codex, Cursor) so it can drive the platform CLI. Nothing to do with your agent's runtime. - [`delegation.skills`](/reference/agent-manifest): the units of work your agent *accepts* from other agents, declared in the manifest. A delegation contract, not a folder of instructions. ## Related - [Session workspace and state](/guides/build/session-workspace-and-state): what else lives in the workspace and what survives a resume. - [Claude Agent SDK](/guides/build/claude-agent-sdk) and [Deep Agents](/guides/build/deep-agents): the harness guides. - [Agent manifest](/reference/agent-manifest): every field, including `delegation.skills`. --- Source: /guides/build/choose-a-harness # Choose a harness Claude Agent SDK or Deep Agents. What each one is good at, what your entrypoint returns, and what stays the same whichever you pick. A harness is the agent framework your code runs inside. Tanomis supports two Python harnesses, and they share everything that is not your code: the manifest, the build, versions, tenant enablement, the session runtime, governed tools, and evidence. Pick by how you want to express the agent. - **Claude Agent SDK**: your entrypoint returns a prompt and options. The smallest entrypoint, Claude's tools, hooks, subagents, and permission callbacks. - **Deep Agents**: your entrypoint returns a compiled LangGraph graph. Typed LangChain tools, middleware, structured output, checkpoint-aware state. - Both are declared with one line in `agent.yaml`, `framework`, and scaffolded by `tanomis agent new`. - Switching harness is a new agent, not a new version; `framework` is part of the contract. ## The choice | | `claude-agents-sdk` | `deepagents` | |---|---|---| | Best fit | A system prompt plus tools, or an existing Claude Agent SDK agent | Tool-heavy or graph-shaped work, or existing LangGraph code | | Entrypoint returns | `(prompt, options)` | A graph from `create_deep_agent` | | Tools | Claude Agent SDK tools, hooks, subagents | Typed LangChain tools and middleware | | State between turns | Claude SDK session continuity | LangGraph checkpoints in the session workspace | | Approvals and questions | `permission_mode: "default"` plus the manifest's list | Manifest list composed with author `interrupt_on` | Choose Claude Agent SDK when the behaviour is naturally a prompt with options and you want the least code. Choose Deep Agents when the agent is a graph of typed tools, needs structured output or middleware, or already exists as LangGraph code. ## What does not change The runtime owns tenant isolation, the working directory, the resolved model and provider credentials, session identifiers, governed connectors, and persistence. You own the instructions, the tool implementations, and the behaviour the manifest permits. The [entrypoint contract](/guides/build/write-your-agent) is the same shape for both. List the harnesses your installed CLI supports rather than copying a value from memory: ```bash title="Terminal" tanomis agent harnesses ``` ## Where to go next - [Claude Agent SDK](/guides/build/claude-agent-sdk): the entrypoint, the ownership boundary, approvals. - [Deep Agents](/guides/build/deep-agents): the graph entrypoint, dependencies, tool interrupts. - [Write your agent](/guides/build/write-your-agent): what comes in and what you return. --- Source: /guides/build/choose-models # Choose models Declare the model an agent uses and the models a run may request, and understand what the tenant's LLM gateway does with every call. An agent never talks to a model provider directly. Every model call leaves the sandbox through the tenant's LLM gateway, which is where model access is authorized, usage is metered per run, and traffic is traced. Your manifest declares which models the agent may use; the gateway enforces it. - `model` is the default the agent runs with. `permitted_models` is the allowlist a run may choose from. - A run that requests a model outside the allowlist is refused at the gateway. - Model IDs are the ones available to your tenant. The same manifest can deploy to tenants with different model lineups only if the IDs exist in each. - Usage is metered per run, so cost and spend are attributable to a session, a run, and the principal that started it. ## Declare the models ```yaml title="agent.yaml" model: "your-workspace-model-id" permitted_models: - "your-workspace-model-id" - "your-workspace-fast-model-id" policy: scope_ceiling: - "llm:your-model-family:invoke" ``` - **`model`** is resolved for a run unless the request overrides it. - **`permitted_models`** bounds overrides. Leaving it empty does not widen access: it means the manifest declares no allowlist and the decision falls to platform and tenant policy. - **`policy.scope_ceiling`** must carry the `llm::invoke` entry for the model family the agent calls. Change the model, update the ceiling, or the declared ceiling stops describing what the agent does. ## Follow the organization default Set `model: default` to run on your organization's default model, chosen on the Console's Models page (or with `tanomis model default set`). The default can be any active model on that page: one of your organization's own models or one of the platform's. Until the organization chooses one, `default` is the platform default. Each new session resolves `default` to the model it points at then, so changing the default moves every agent that follows it without a new version; open sessions keep the model they started with. If the chosen model stops being available, sessions that follow the default are refused with the reason, and the Models page warns, until someone chooses again. A concrete model ID pins the agent to that model, whatever the default is. ```yaml title="agent.yaml" model: default permitted_models: - default ``` `permitted_models` may list `default` alongside concrete IDs; it admits the one model the default resolves to for the session. `tanomis agent new` scaffolds `model: default` for the Claude harness. ## Override per session or run A session can be created with a `model` from the permitted set, and a run can request one the same way. An override outside the set is a `400` at request time, before any work starts. ```json title="POST /v1/sessions" { "agent_name": "support-copilot", "model": "your-workspace-fast-model-id" } ``` ## What the gateway does with a call | Concern | What happens at the gateway | |---|---| | Authorization | The model is checked against the version's allowlist and tenant policy. A refusal is a policy decision recorded in the audit trail. | | Metering | Tokens and requests are attributed to the run, so spend rolls up to sessions, agents, and principals. | | Tracing | Each call becomes a span in the run's execution trace, on the tenant's own trace plane. | | Isolation | The gateway is per tenant. Traffic from one tenant never shares a gateway with another. | You do not configure the gateway. It is provisioned with the tenant; the manifest is the only input you control. ## Choose between models A single agent can hold a capable default and a faster, cheaper model in its allowlist, and let the calling application pick per request. Keep the allowlist short: every entry is a model the agent is authorized to call, and the ceiling entry must cover its family. `model` and `permitted_models` are part of the version's contract. To add a model, deploy a new version and roll it out; a running version's list never changes. ## Where to go next - [Agents and versions](/concepts/agents-and-versions): what else the version contract carries. - [Policy, approvals, and evidence](/concepts/governance): where the model decision sits among the other ceilings. - [Agent manifest](/reference/agent-manifest#models): the field reference. --- Source: /guides/build/claude-agent-sdk # Claude Agent SDK Write the Claude Agent SDK entrypoint, know what the runtime owns, and wire approvals and questions through the manifest. With the Claude Agent SDK harness your whole agent is one callable that returns a prompt and the options Claude should run with. The runtime supplies the model, the workspace, credentials, and session continuity, and composes the platform's approvals with your own tool restrictions. - Scaffold with `tanomis agent new NAME --harness claude-agents-sdk`. - `create_agent(input_payload, context)` returns `(prompt, options)`; options can be a plain dict, so the module imports without the runtime. - Never set a model or working directory in the entrypoint; the manifest and runtime own them. - Keep `permission_mode: "default"` so the platform's approval-required tools can pause the run. ## Scaffold ```bash title="Terminal" tanomis agent new support-copilot --harness claude-agents-sdk --agents-root ./agents ``` ## The entrypoint ```python title="agents/support-copilot/agent.py" from __future__ import annotations def create_agent(input_payload, context): prompt = input_payload.get("prompt", "Triage this request.") return ( prompt, { "system_prompt": ( "You are a support triage agent. Return severity, a short summary, " "and the next best action." ), "permission_mode": "default", "max_turns": 12, }, ) ``` The callable may also accept only `context`, accept no arguments, or be async. A plain options dictionary keeps your module importable outside the managed runtime, which is what the generated contract test relies on. ## Who owns what | Concern | Owner | |---|---| | System prompt, allowed and disallowed tools, max turns, hooks, subagents | You | | Workspace root and current working directory | Runtime | | Model resolution and provider credentials | Runtime | | Session ID, resume state, partial-message streaming | Runtime | | Tool decision callbacks | Composed: your restrictions first, then platform approvals | | Governed connector configuration | Runtime | Do not set a model or working directory in the entrypoint to bypass the manifest. The runtime resolves both for the tenant and version, and a run that tries to override them is refused. ## Approvals and questions Name the tools that need a person in `agent.yaml`, and let the harness ask questions: ```yaml title="agent.yaml" policy: approval_required_tools: - Bash enable_ask_user: true ``` Keep `permission_mode: "default"`. `bypassPermissions` skips the SDK callback path and is not compatible with governed approvals. See [Approval-required actions](/guides/run/approval-required-actions) for what happens when the run pauses. ## Validate and deploy ```bash title="Terminal" cd agents/support-copilot uv run python -m unittest tests/test_agent_contract.py tanomis agent deploy . --tenant northwind-sandbox --dry-run ``` ## Where to go next - [Write your agent](/guides/build/write-your-agent): the contract every harness shares. - [Manifest essentials](/guides/build/manifest-essentials): models, tools, hosts, limits. - [Test before you deploy](/guides/build/test-before-you-deploy): the sandbox tenant. --- Source: /guides/build/connect-your-models # Connect your models Bring your own model endpoints or provider accounts and route agents to them through the tenant gateway. Agents call models through the tenant's LLM gateway. Registering your own provider accounts or endpoints behind that gateway, so agents use your keys, quotas, and regions, is planned. This page is planned and not yet written, because the capability it describes is not yet generally available. The structure is here so you can see where it fits; the notice comes off when it ships. ## What this page will cover - Register a provider or endpoint for the organization. - Expose it to tenants as model IDs agents can declare. - Failover and aliasing between providers. - What stays enforced: allowlists, metering, tracing. ## Meanwhile - [Choose models](/guides/build/choose-models) covers the models available to your tenant today. --- Source: /guides/build/deep-agents # Deep Agents Return a compiled Deep Agents graph from the entrypoint while Tanomis owns the model route, checkpoints, and the workspace. With the Deep Agents harness your entrypoint returns a compiled LangGraph graph built by `create_deep_agent`. Tanomis resolves the tenant-approved model before the graph is created, roots the checkpointer and filesystem backend in the session workspace, and turns the manifest's approval list into tool interrupts. - Scaffold with `tanomis agent new NAME --harness deepagents`. - Pass `context.runtime_context.model` to `create_deep_agent` unchanged; it is already the resolved, provider-routed model. - Keep the generated dependencies: Deep Agents, the workspace SQLite checkpointer, and the provider adapters the managed route needs. - Tools named in `policy.approval_required_tools` become interrupts; approve, reject, and answers are supported, edited arguments are not. ## Scaffold ```bash title="Terminal" tanomis agent new research-assistant --harness deepagents --agents-root ./agents ``` ## The entrypoint ```python title="agents/research-assistant/agent.py" from __future__ import annotations def create_agent(input_payload, context): from deepagents import create_deep_agent def classify_request(text: str) -> str: """Classify a request as question, incident, or feedback.""" normalized = text.lower() if "error" in normalized or "failed" in normalized: return "incident" if "?" in text: return "question" return "feedback" return create_deep_agent( model=context.runtime_context.model, tools=[classify_request], system_prompt=( "Classify the request, explain the evidence, and recommend one next action." ), ) ``` Import `deepagents` inside the callable so the module imports without the SDK, which is what the generated contract test relies on. Pass `context.runtime_context.model` through unchanged. ## Keep the generated dependencies The scaffold pins Deep Agents, the workspace SQLite checkpointer, and the provider adapters the managed model route requires. Do not remove an adapter because the manifest's model name looks like a different provider; the gateway may route it there. Lock before you deploy: ```bash title="Terminal" cd agents/research-assistant uv sync --frozen uv lock --locked ``` ## Who owns what | Concern | Owner | |---|---| | Tools, system prompt, middleware, structured output, subagents | You | | Workspace root and the runtime session or thread ID | Runtime | | Default checkpointer and persistent store | Runtime | | Resolved model and provider credentials | Runtime | | Filesystem backend root and platform deny rules | Runtime | | Additional in-workspace backend routes | You, within manifest policy | | Tool interrupts | Composed from the manifest's approvals and your configuration | The generated manifest uses `workspace-sqlite` checkpointing and the workspace-composite backend. Keep those defaults for the first deployment. ## Approval-aware tools ```yaml title="agent.yaml" policy: approval_required_tools: - execute enable_ask_user: true ``` The runtime combines that list with your own `interrupt_on` configuration. The approval surface supports approve, reject, and user answers; do not design a tool that needs edited arguments as an approval outcome. ## Where to go next - [Write your agent](/guides/build/write-your-agent): the contract every harness shares. - [Manifest essentials](/guides/build/manifest-essentials): models, tools, hosts, limits. - [Approval-required actions](/guides/run/approval-required-actions): what a paused run looks like from your application. --- Source: /guides/build/generative-ui # Generative UI Let an agent return structured UI, rendered by the Console or your own application, instead of only text and tool calls. Today an agent's output is text, tool calls, and files. Generative UI lets an agent emit typed UI elements, such as forms, tables, choices, and progress views, that the Console or your own application renders, with the user's response flowing back into the run. This page is planned and not yet written, because the capability it describes is not yet generally available. The structure is here so you can see where it fits; the notice comes off when it ships. ## What this page will cover - The UI element types an agent can emit and how they appear in the event stream. - Rendering elements in the Console and in your own application. - Returning user input to the run and how it relates to approvals. - What is recorded in evidence when a user interacts with generated UI. ## Meanwhile - [Stream and replay events](/guides/run/stream-and-replay-events) covers the event stream generated UI would ride on. - [Approval-required actions](/guides/run/approval-required-actions) is the structured user interaction the platform supports today. - [Answer approvals in the Console](/guides/run/answer-approvals-in-the-console) shows how the Console renders that interaction. --- Source: /guides/build/manifest-essentials # Manifest essentials Understand the agent.yaml fields that control identity, state, models, resources, and policy. `agent.yaml` is the deployable contract for one agent. Start with `tanomis agent new`; the generated manifest carries safe defaults and a minimal model authorization grant. This page covers the fields you touch first. For every accepted setting — including MCP connectors, delegation, capability metadata, and the full validation rules — see the [Agent manifest reference](/reference/agent-manifest). - `name`, `version`, `framework`, `entrypoint`, `kind`, and `identity_mode` identify the agent and its behaviour. - `persistent: true` and `session` limits shape the workspace; `resources` reserve capacity. - `model` and `permitted_models`, `network.egress`, and `policy` declare what the version may reach; every one is a ceiling the tenant can narrow. ## Identity and behavior ```yaml name: support-copilot version: 0.1.0 framework: claude-agents-sdk entrypoint: agent:create_agent kind: interactive identity_mode: service ``` - `name` is the stable agent identity. - `version` labels an immutable build. - `framework` selects the harness adapter. - `entrypoint` is `python_module:function`. - `kind` describes interactive or background behavior. - `identity_mode` declares the intended attribution posture: `service`, `delegated`, or `hybrid`. Hosted deployments require it explicitly, and the first non-rejected version pins it, so choose with your organization administrator before production use. Read [Agent identity and access](/concepts/identity-and-access) before choosing: availability and enforcement differ by path, and a mode grants nothing by itself. ## Persistence and warm starts ```yaml persistent: true session: idle_timeout: 30m max_lifetime: 24h ``` `persistent` provisions durable tenant-scoped storage and is required for new hosted versions. Hosted sessions are moving to one managed worker boundary. Within that rollout, a snapshot can make startup faster, but it does not select a different runtime and is not controlled by the agent manifest. Older manifests may still contain `cr_enabled`. The platform accepts it temporarily for internal rolling-deployment compatibility, but current runtime selection does not consult it. The deploy pipeline and CLI still use `true` temporarily to request a golden build until their follow-up cutover. During that rollout, a server may require `cr_enabled: true` on a manifest that declares egress or settlement; this admission check disappears after universal-worker and golden-policy cutover. The harness owns its checkpoint format while the runtime owns the workspace and lifecycle. ## Models ```yaml model: "your-workspace-model-id" permitted_models: - "your-workspace-model-id" ``` `model` is the default resolved model. `permitted_models` is the runtime allowlist for overrides. Use model IDs available to the selected tenant. When changing the model, update the matching `llm::invoke` scope emitted by the scaffold. ## Resource requests ```yaml resources: cpu: 100m memory: 512Mi ``` These are scheduling requests, not the burst ceiling. Leave them at the scaffolded values until measurements show a reason to change them. Optional `cpu_limit` and `memory_limit` raise the per-agent ceiling when tenant policy allows it. ## Network egress ```yaml network: egress: - github.com ``` The external hostnames this agent may reach. Entries are bare, lowercase hostnames, and an entry covers its subdomains — `github.com` admits `api.github.com`. Wildcards, addresses, and in-cluster names are refused at deploy. The platform's own destinations — the managed model gateway and governed connector routes — are always available and are never declared here; your list only adds to them. Your administrator can set a tenant ceiling that narrows what you declare. On the managed worker path, egress is enforced identically on snapshot restore and clean bootstrap; if that boundary cannot be established, a session with author-declared egress is refused rather than sent to the transitional ordinary path. > **Availability:** Author-declared egress is enabled per tenant. See [What is available where](/reference/availability) and the [manifest reference](/reference/agent-manifest#network) for the grammar and enforcement points. ## Approvals and authorization ```yaml policy: approval_required_tools: - Bash enable_ask_user: true scope_ceiling: - "llm:your-model-family:invoke" allowed_audiences: - "https://your-workspace-api.example" ``` - `approval_required_tools` pauses listed tools for a human decision. - `enable_ask_user` exposes the harness question flow. - `scope_ceiling` declares the maximum set of concrete capabilities a scoped token may carry. Wildcard grants are not accepted. - `allowed_audiences` declares HTTPS resource audiences for scoped tokens and is required when a scope ceiling is present. > **Availability:** These authorization declarations validate and record the intended contract, > but scoped service-token acquisition and delegated acquisition are currently held: no supported > public runtime workflow uses them. Do not infer a token-minting setup from this example. Read > [Agent identity and access](/concepts/identity-and-access#what-the-service-assertion-is-and-is-not) > before designing scoped authority. The first two fields emit runtime interactions, not token-exchange approvals. Once the version is deployed, follow [Handle approval-required actions](/guides/run/approval-required-actions) to list, validate, answer, and audit those durable questions and tool decisions through the Platform API. When a supported integration makes these fields actionable, use only the audience supplied for your tenant/environment. Never copy one from another environment. ## Harness settings Keep only the block matching `framework`: ```yaml harness: claude-agents-sdk: {} ``` or: ```yaml harness: deepagents: is_async: true stream_modes: [messages, updates, custom] checkpointer: workspace-sqlite backend: workspace-composite recursion_limit: 50 ``` Validate the whole contract without uploading: ```bash tanomis agent deploy . --tenant northwind-sandbox --dry-run ``` ## Connect tools across workspaces Declare `integrations` with the published definition and contract, `identity: user` or `workspace`, and `tools.allow`. Required integrations block execution until configured; use `required: false` only when the agent can work without those tools. Customer connection IDs and credentials do not belong in the manifest. Configure them in **Agent → Integrations** after publishing the version. See the [portable MCP fields](/reference/agent-manifest#governed-mcp-connectors) and [workspace setup guide](/guides/connect-tools/mcp-connectors). ## Where to go next - [Agent manifest](/reference/agent-manifest): every field, default, and validation rule. - [Choose models](/guides/build/choose-models): the model allowlist and the gateway. - [Test before you deploy](/guides/build/test-before-you-deploy): the dry run and the sandbox. --- Source: /guides/build/secrets-and-configuration # Secrets and configuration Why an agent bundle never carries a credential, how the agent reaches external systems anyway, and where per-run configuration comes from. An agent does not hold secrets. Model access, tool access, and identity are supplied by the platform at run time, scoped to the tenant and the version, and recorded. What you ship is behaviour and a contract; what the platform adds is authority. - The deploy preflight excludes secret-looking files from the bundle and warns about them. - Models are reached through the tenant's gateway with the platform's provider credentials. - External tools are reached through governed connectors, whose credentials the platform holds. - Per-run configuration comes from the run's `input` and the session's `agent_inputs`, both JSON your application controls. ## Nothing secret in the bundle `tanomis agent deploy` flags `.env`, `*.pem`, `*.key`, and any file whose name contains `credentials`, and excludes them from the package. The manifest never carries a secret either: `mcp.connectors` references a connector by id, and the connector owns any credential reference. If a credential was committed by mistake, rotate it; excluding it from the next bundle does not undo the commit. ## How the agent reaches things | Need | How it is supplied | |---|---| | A model | The tenant's LLM gateway, with the platform's provider credentials. You declare which models; you never see a provider key. | | An external tool | A governed MCP connector. The connector's service credential, or a member's private connection, is presented at the gateway per call. | | Another agent | A delegation, authorized per call by the control plane. | | The platform's own APIs | A per-run identity assertion custodied outside the agent process. It authenticates the run to the platform; it is not an API key you can copy. | An agent that needs an API the platform does not front should get it through a connector, so the call is allowlisted, metered, and recorded, rather than by embedding a key. ## Configuration per run and per session Your application passes configuration as data: ```json title="POST /v1/sessions" { "agent_name": "support-copilot", "agent_inputs": { "locale": "en-GB", "tone": "formal" } } ``` ```json title="POST /v1/sessions/{session_id}/runs" { "input": { "prompt": "Draft a reply.", "case_id": "1842" } } ``` `agent_inputs` is session-scoped initialisation data; `input` is per run. Both reach your entrypoint. Treat them as untrusted input and validate them; they are not a place for secrets either, because they are stored with the session and visible in evidence at the tenant's capture level. ## Environment-specific values Do not branch on environment in code. The same version deploys to development and production tenants; what differs is the tenant's policy, ceilings, and connectors. If behaviour must differ, pass it through `agent_inputs` or ship a separate version. ## Where to go next - [MCP connectors](/guides/connect-tools/mcp-connectors): how tool credentials are owned. - [Choose models](/guides/build/choose-models): the gateway. - [Identity and access](/concepts/identity-and-access): the run identity the platform supplies. --- Source: /guides/build/session-workspace-and-state # Session workspace and state What the agent can write to, what persists across runs and sessions, and how idle and lifetime limits shape a session. Each session has its own workspace directory. Runs in the session read and write it; a new session starts clean. Files you attach land there, and the agent's harness state lives there too. Nothing in the workspace is shared across tenants, and nothing is baked into the version. - The workspace is `/workspace`, mounted per session over durable tenant-scoped storage. - State persists across **runs** in a session, and across suspend and resume. A **new session** starts empty. - `session.idle_timeout` and `session.max_lifetime` bound how long a session lives. - Warm starts restore the harness process, not your data: the golden snapshot contains no tenant state. ## What lives in the workspace | Content | Written by | Persists | |---|---|---| | Files you attach with the Files API | Your application | For the session | | Files the agent writes | The agent | For the session | | Harness state: conversation continuity, tool results | The harness | For the session, across runs and suspend/resume | | A project `CLAUDE.md`, when `enable_claude_md` is on | You, in the bundle | Read as a context file | | [Agent skills](/guides/build/agent-skills) at `.claude/skills/`, when your agent ships a `skills/` directory | The platform, from your image | Rebuilt from the image each run | The workspace belongs to one session. A later run continues it; a fork or another session does not see it. Files are first-class resources with their own ids and integrity checks, so an upload is never a blind write; see [Attach files to a session](/guides/run/attach-files). ## Declare persistence and limits ```yaml title="agent.yaml" persistent: true session: idle_timeout: 30m max_lifetime: 24h ``` - `persistent: true` provisions durable tenant-scoped storage and gives each session its own directory. It is required for new hosted versions. - `idle_timeout` is how long a session may sit with no run before it is expired. - `max_lifetime` is the longest a session may exist, idle or not. Both are compact durations that resolve to whole seconds: `45s`, `30m`, `24h`. An expired session can be resumed explicitly; see [Sessions and runs](/concepts/sessions-and-runs). ## Idle is cheap An idle session is suspended: its harness process is checkpointed and the worker released, and it is restored in well under a second when the next run arrives. The workspace stays where it is. Your application does not see the difference; the session status stays `idle` either way, and you never call resume for it. ## What is not state - **The version's contract** is immutable and lives in the registry, not the workspace. - **Credentials** are never in the workspace. Connector credentials are held by the platform and presented at the gateway; see [Secrets and configuration](/guides/build/secrets-and-configuration). - **Evidence** is recorded outside the session: the audit trail and the execution trace survive the session's expiry. ## Where to go next - [Attach files to a session](/guides/run/attach-files) - [Agents and versions](/concepts/agents-and-versions): warm starts and snapshots. - [Agent manifest](/reference/agent-manifest#state-warm-starts-and-sessions): the fields. --- Source: /guides/build/test-before-you-deploy # Test before you deploy What you can verify locally, what only a deployed run can show, and how to use a sandbox tenant as your test bed. There is no local platform mode: the runtime, the gateway, the connectors, and the policy checkpoints exist only in a tenant. Testing is therefore two layers: what you can check on your machine in seconds, and what you check with a real run in the sandbox tenant. - **Locally:** the contract test, your own unit tests, and `tanomis agent deploy --dry-run`. - **In the sandbox tenant:** `tanomis session run` and runs from your application, with the real gateway, connectors, and approvals. - **Before customers:** the evaluation gate and a canary. ## Locally, in seconds ```bash title="Terminal" cd agents/support-copilot uv run python -m unittest tests/test_agent_contract.py tanomis agent deploy . --tenant "$TANOMIS_TENANT" --dry-run ``` The generated contract test imports your module and checks the entrypoint's shape. Add your own tests beside it for prompt construction, tool functions, and input validation; your entrypoint is plain Python and imports without the runtime. The dry run validates the manifest, the lock policy, and the packaged inputs against the tenant's ceilings, uploads nothing, and ends with "no changes made". Run it on every pull request; see [Deploy from CI](/guides/deploy/deploy-from-ci). What a local test cannot show: model behaviour, connector calls, approvals, egress refusals, session state. Those need a tenant. ## In the sandbox tenant Deploy to the sandbox tenant, which serves no customer, and run the agent the way your application will: ```bash title="Terminal" tanomis agent deploy . --tenant northwind-sandbox tanomis session run support-copilot --tenant northwind-sandbox \ --prompt "Customer reports that CSV exports stop at 80%. Draft a first response." ``` Then exercise the edges: a prompt that should trigger an approval, a tool call outside the connector allowlist, a host outside `network.egress`. Each refusal shows up in the run's events and in the audit trail, which is the behaviour you are testing. For application-side flows, point your integration at the sandbox tenant's workspace and follow [Invoke from your application](/guides/run/invoke-from-your-application); the API is the same in every tenant. ## Before customers Score the candidate against the version you trust with [Evaluate a version](/guides/evaluate/evaluate-a-version), then canary it for one or two customers with [Roll out and roll back](/guides/deploy/rollout-and-rollback). A version that passed in the sandbox is the same immutable build that reaches customers; only each tenant's policy differs. ## Where to go next - [Environments and contexts](/guides/deploy/environments-and-contexts): the sandbox beside your customers. - [Session workspace and state](/guides/build/session-workspace-and-state) --- Source: /guides/build/write-your-agent # Write your agent The entrypoint contract every harness shares, what the runtime passes in, what you return, and what stays out of your code. An agent on Tanomis is one Python module with one callable. The runtime calls it with the run's input and a context, and you return what the harness needs to start. Everything else, the model route, the workspace, credentials, session continuity, governed tools, is supplied by the runtime. - The entrypoint is `module:callable`, declared in `agent.yaml` as `entrypoint`. - It receives `input_payload` (the run's `input` JSON) and `context` (the runtime context). - With Claude Agent SDK it returns `(prompt, options)`; with Deep Agents it returns a compiled graph. - It must not choose a model, a working directory, or credentials. Those are the manifest's and the runtime's. ## The entrypoint ```yaml title="agent.yaml" entrypoint: agent:create_agent ``` ```python title="agent.py" from __future__ import annotations def create_agent(input_payload, context): ... ``` The callable may accept both arguments, only `context`, or none, and it may be `async`. Keep the module importable outside the managed runtime so the contract test can import it. ## What comes in | Argument | What it holds | |---|---| | `input_payload` | The run's `input` object exactly as your application sent it, for example `{"prompt": "..."}`. Its shape is yours to define; document it for the callers of your agent. | | `context` | The runtime context: the resolved model under `context.runtime_context.model`, the session, and the workspace. Pass the model through unchanged; the runtime resolved it for the tenant and version. | ## What you return | Harness | Return | Notes | |---|---|---| | `claude-agents-sdk` | `(prompt, options)` | `options` is a plain dictionary: system prompt, allowed and disallowed tools, max turns, hooks, subagents, permission mode. | | `deepagents` | The compiled graph from `create_deep_agent` | Tools, middleware, and graph state are yours; the model comes from `context.runtime_context.model`. | See [Claude Agent SDK](/guides/build/claude-agent-sdk) and [Deep Agents](/guides/build/deep-agents) for a complete example of each. ## Who owns what | Concern | Owner | |---|---| | Behaviour: prompts, tools, turns, hooks, subagents, graph | You | | Model resolution and provider credentials | Runtime, from `model` and `permitted_models` | | Workspace root and working directory | Runtime | | Session identity, resume, and streaming | Runtime | | Tool decisions | Composed: your restrictions first, then platform approvals | | Governed connector configuration | Runtime, from `mcp.connectors` | Setting a model or a working directory in code to bypass the manifest does not work: the runtime resolves those for the tenant and version, and the manifest is the contract that was reviewed. ## Ask for a person when it matters Two manifest switches turn moments in your agent into durable interactions: list tools in `policy.approval_required_tools` to pause for a permission decision, and set `policy.enable_ask_user: true` to let the agent ask a question. Your code does not implement the pause; the harness emits it and the runtime holds it. See [Approval-required actions](/guides/run/approval-required-actions). ## Where to go next - [Manifest essentials](/guides/build/manifest-essentials): the fields you touch first. - [Session workspace and state](/guides/build/session-workspace-and-state): what persists between runs. - [Test before you deploy](/guides/build/test-before-you-deploy): the contract test and the dry run. --- Source: /guides/connect-tools/create-a-custom-connector # Create a custom connector Register an MCP server as a governed connector for your tenant, discover and review its tools, activate it, and configure agent access. A connector is how an agent reaches an MCP server without launching one itself. A tenant administrator registers the server once; the platform discovers its tools, you review them, and agents declare the tools they need by referencing this connector in their manifest. > **Availability:** Custom connectors are enabled per tenant and need the `manage` capability in your workspace. See [What is available where](/reference/availability). 1. **Create** the connector with its endpoint and authentication mode. 2. **Test** reachability and **discover** its tool inventory. 3. **Review** each tool you intend to expose. 4. **Activate** the connector. 5. **Configure agent access** using a direct connector declaration. ## 1. Create From the Console, open Connectors and choose Add connector. Where the updated ownership controls are available, choose **Workspace** or **Platform-wide** explicitly. Workspace ownership belongs to the selected workspace; platform-wide ownership requires platform administration authority and the **Platform** context. Ownership describes who manages the connector, not which upstream data a user may access. From the CLI, inspect the supported scope and tenant flags: ```bash title="Terminal" tanomis mcp connector create --help ``` A connector has a name, the server endpoint, and an authentication mode: service authentication with a credential reference the connector owns, or none for an unauthenticated endpoint. Secret material is never placed in an agent manifest. ## 2. Test and discover ```bash title="Terminal" tanomis mcp connector test CONNECTOR_ID tanomis mcp connector discover CONNECTOR_ID tanomis mcp connector tools CONNECTOR_ID ``` `test` probes reachability and `tools/list`. `discover` records the tool inventory and prints the diff since the last discovery, so a server that changed a tool's schema shows up before an agent trips over it. `tools` lists the discovered inventory with the names you will use in allowlists. ## 3. Review ```bash title="Terminal" tanomis mcp connector review CONNECTOR_ID --tool create_issue --status approved ``` A tool's review status is what makes it available to agents. Review is per tool, so a server with thirty tools exposes only the ones you approved. ## 4. Activate ```bash title="Terminal" tanomis mcp connector set-status CONNECTOR_ID active ``` Activation is an explicit step; probing and discovery never change status. The server enforces the transition rules and the authorization; the CLI only refuses values outside the vocabulary. ## 5. Make the connection available to an agent Reference the connector directly in `agent.yaml`: ```yaml integrations: support: connector: tenant/support-desk tools: allow: [search_tickets] ``` Use the actual connector name and approved tools. Run `tanomis mcp connector tools CONNECTOR_ID` to inspect them. Deploy the agent; there is no catalog association or connection-selection step. See [the manifest reference](/reference/agent-manifest#governed-mcp-connectors). ## Where to go next - [MCP connectors](/guides/connect-tools/mcp-connectors): workspace setup and runtime authorization. - [Tool approvals and schema pins](/guides/connect-tools/tool-approvals-and-schema-pins): reviewed tool definitions. - [`tanomis mcp connector`](/cli/mcp/connector): endpoint administration commands. --- Source: /guides/connect-tools/github-repositories # GitHub repositories Connect GitHub to a workspace, set which repositories agents may use, and declare repository access in a manifest. Agents work on GitHub with `git` and `gh`, using a token the platform issues for each run. Three things decide what a run gets: 1. **The connection.** A workspace admin connects a GitHub organisation or account by installing the platform's GitHub App on it. 2. **The ceiling.** The workspace admin says which repositories agents may be given, and the most they may do there. 3. **The manifest.** The agent names the repositories it needs under `access.github`. A run gets exactly the repositories its manifest names, limited by the ceiling and by what the installation can see. Nothing is granted per person or per agent outside the manifest. ## Connect GitHub In the Console, open **Settings → Organization → GitHub** and choose **Connect GitHub**. GitHub asks you to pick the organisation or account and the repositories the App may see, then asks you to sign in once. The sign-in confirms you own that account (an organisation owner, or the user it belongs to); the platform keeps no copy of your GitHub token. From the CLI, `tanomis github connect` prints the same link. The first connection allows agents to **read** every repository the installation can see. Adding or removing repositories on GitHub updates the connection automatically. ## Set the ceiling The ceiling lists repositories, or `owner/*` patterns, with the most any agent may be given. A specific repository overrides its owner's pattern. ```bash tanomis github ceiling set 'anyfind/*' --can read tanomis github ceiling set anyfind/agent-platform-website-tanomis --can read,push,pull_request tanomis github status ``` ## Declare access in the agent ```yaml title="agent.yaml" access: github: repos: anyfind/agent-platform-website-tanomis: can: [read, push, pull_request] ``` Deploy as usual. A declaration the workspace cannot honour is refused with the reason, for example: ```text access.github: cannot be used in workspace acme: Asks for push and pull_request on anyfind/agent-platform-website-tanomis; this workspace allows read (anyfind/*). Raise the ceiling in Settings → Organization → GitHub, or narrow the manifest. ``` `tanomis agent access NAME` shows what an agent may reach in the current workspace, using the same status the Console shows. See the [manifest reference](/reference/agent-manifest#github-repositories) for capabilities and slots for global agents. ## What a run sees Inside the sandbox, `git` and `gh` authenticate automatically for the declared repositories only. With more than one repository, `gh` outside a clone needs `-R owner/name`. Every `git` or `gh` operation asks for a fresh token, so lowering the ceiling or disconnecting GitHub stops the next operation. Tokens expire within an hour and are revoked when the run ends. --- Source: /guides/connect-tools/integration-contracts # Declare an integration Reference a connector and its allowed tools directly in agent.yaml. An agent references a configured connector directly. No catalog association, contract publication, or connection-selection step is needed. ```yaml title="agent.yaml" integrations: finance: connector: global/alphavantage-workspace required: true tools: allow: [SYMBOL_SEARCH, COMPANY_OVERVIEW, EARNINGS] ``` Find the connector and its approved tools: ```bash tanomis mcp connector list tanomis mcp connector tools CONNECTOR_ID ``` Use `global/NAME` for a platform connector, `tenant/NAME` for a connector owned by the agent's workspace, or an exact connector ID. An unqualified name works when unambiguous. A global agent can reference global connectors; publish a tenant-scoped agent when it needs a tenant-owned connector. The integration map key is just the agent's local name. The connector supplies the endpoint, credentials and shared/personal account mode. Personal connectors still require each caller to connect their own account. Credentials never belong in the manifest. An optional `identity: workspace` or `identity: user` can assert the expected mode; it must match the connector. `tools.allow` is the maximum set the agent may use. Optional `tools.approval_required` lists tools that require confirmation for each call and must be a subset of `tools.allow`. The connector's current permitted tools and account access can narrow this set, never expand it. At registration, the platform records the connector ID and approved tool schemas automatically. These platform-owned details appear separately from author configuration in the Manifest tab. Deleting and recreating a connector with the same name does not redirect an existing version. ## Changes and availability - Changing the connector or allowed tools requires a new agent version. - Rotating credentials on the same connector does not require a new version. - Changed tool schemas require review and a new agent version. - **Agent → Integrations** shows the declared connector, readiness and expandable allowed tools. Operators can disable or enable access for the agent; they cannot substitute another connector. - A required connector that is unavailable blocks execution. An unavailable optional connector is omitted. - Disabled access remains disabled when publishing another version or migrating an old declaration. Existing catalog-based agent versions remain compatible. New agents should use the direct connector declaration above. No attachment creation or approval workflow is involved. --- Source: /guides/connect-tools/mcp-connectors # MCP connectors Reference configured connectors in your manifest, limit tools, and connect personal accounts. An agent references a configured connector directly. No catalog association, contract publication, or connection-selection step is needed. ```yaml title="agent.yaml" integrations: finance: connector: global/alphavantage-workspace required: true tools: allow: [SYMBOL_SEARCH, COMPANY_OVERVIEW, EARNINGS] ``` Find the connector and its approved tools: ```bash tanomis mcp connector list tanomis mcp connector tools CONNECTOR_ID ``` Use `global/NAME` for a platform connector, `tenant/NAME` for a connector owned by the agent's workspace, or an exact connector ID. An unqualified name works when unambiguous. A global agent can reference global connectors; publish a tenant-scoped agent when it needs a tenant-owned connector. The integration map key is just the agent's local name. The connector supplies the endpoint, credentials and shared/personal account mode. Personal connectors still require each caller to connect their own account. Credentials never belong in the manifest. An optional `identity: workspace` or `identity: user` can assert the expected mode; it must match the connector. `tools.allow` is the maximum set the agent may use. Optional `tools.approval_required` lists tools that require confirmation for each call and must be a subset of `tools.allow`. The connector's current permitted tools and account access can narrow this set, never expand it. At registration, the platform records the connector ID and approved tool schemas automatically. These platform-owned details appear separately from author configuration in the Manifest tab. Deleting and recreating a connector with the same name does not redirect an existing version. The gateway checks current access on every tool call. Disabling an agent integration or revoking a personal credential blocks subsequent calls without waiting for token expiry. ## Changes and availability - Changing the connector or allowed tools requires a new agent version. - Rotating credentials on the same connector does not require a new version. - Changed tool schemas require review and a new agent version. - **Agent → Integrations** shows the declared connector, readiness and expandable allowed tools. Operators can disable or enable access for the agent; they cannot substitute another connector. - A required connector that is unavailable blocks execution. An unavailable optional connector is omitted. - Disabled access remains disabled when publishing another version or migrating an old declaration. Existing catalog-based agent versions remain compatible. New agents should use the direct connector declaration above. No attachment creation or approval workflow is involved. ## See which agents use a connector Open **Connectors → a connector → Used by**. Each agent version appears once with its declared tools. ## Read the four independent gates | Gate | What must be true | |---|---| | Manifest declaration | The version names this connector and the requested tool. | | Connection readiness | The connector is active, approved tool schemas match, and any personal account is ready. | | Agent access | Access is enabled for this agent and the connector permits the declared tools. | | Current call authorization | The caller, active run and any required human confirmation permit this exact call. | Workspace identity: administrator → workspace connection → connector-owned service authority (credential reference when required; none for an unauthenticated endpoint) → manifest-limited tool access → external system. Personal identity: member → own account on the declared connector → that member’s private credential → manifest-limited tool access → external system. A member cannot register a custom endpoint through personal sign-in. Platform administrators manage platform connectors; authorized workspace administrators manage connectors in their workspace. Neither registering an endpoint nor connecting a personal account overrides the manifest's tool limits, connector restrictions, an agent disable, or required call approval. ## Resume after connecting an account If account setup interrupts a request, Console retains the prompt and attachments for up to 24 hours. Connect your account, return to the saved request, and click **Continue**. Returning from OAuth or refreshing status does not execute the request automatically. **Discard request** removes it. --- Source: /guides/connect-tools/private-connections # Private connections and OAuth Connect your own account, recover a saved request, and disconnect access. A personal integration uses your account at the external service. The agent manifest selects the connector and allowed tools; you authorize your own account separately. Your personal credential is not a shared workspace credential and is not put in `agent.yaml`. > **Availability:** This guide applies to supported personal connectors where > integrations are enabled. See [Availability](/reference/availability). ## Connect your account 1. Select the workspace where you will use the agent. 2. Open **Agents → your agent → Integrations** and select the intended version. 3. If the declared connector is disabled or unavailable, ask an administrator to repair it. Signing in does not override connector restrictions or an agent disable. 4. Choose **Connect your account**. Console opens the configured connection; choose **Connect** and complete the provider's OAuth flow. An existing provider authorization may be reused. 5. Check the returned account label. Wait for connection preparation to finish if it is still in progress, then return to the agent or saved request. The provider decides which consent or installation steps are required. GitHub sign-in alone is not proof that its App can access a particular repository. This MCP guide does not configure native Git clone, push, or PR creation. ## Continue a saved request When a required personal integration is unavailable, Console saves your exact prompt and attachment references instead of running the agent. **Continue** stays disabled until setup is ready. Returning from OAuth does not start a run. Return to the same request and click **Continue** once. Concurrent retries do not create additional executions of that saved request. Saved requests expire after 24 hours. **Discard request** deletes the saved request without running it; it does not delete the separately uploaded files. Cancelling OAuth leaves the request blocked. Use the same signed-in user and workspace to return to your saved request. ## Reconnect or check preparation If your account needs authorization again, use the connection's Connect action and complete the provider flow. If Console says your account is connected but the connection is still being prepared, use **Check setup** or refresh status. Repeated OAuth consent is not a remedy for route preparation. A missing or disabled workspace grant needs an administrator, not another personal sign-in. ## Disconnect Open the connection in **Connectors**, choose **Disconnect** for your account, and confirm. This revokes your platform connection; it does not delete the shared connector or another user's account. Subsequent portable tool calls using that credential are denied. An operation already accepted by the provider is not undone. A new request requiring this account stays blocked until you reconnect and explicitly continue. Administrators can separately remove the agent's access in **Agent → Integrations**. A successful personal connection does not override that decision. Personal-account sessions use the verified run initiator's account and require that person to own the session; they do not borrow another member's credential. --- Source: /guides/connect-tools/tool-approvals-and-schema-pins # Tool approvals and schema pins Choose approved connector tools and require confirmation for individual calls. Connector access combines the **reviewed tool inventory**, the **agent's declared tools**, and **current permission to use the declared connector**. None of these alone authorizes a call. Read [Declare an integration](/guides/connect-tools/integration-contracts) for the simple authoring workflow. Tool review approves definitions; confirmation approves an individual call. The declared connector and any required personal account must be ready for execution. ## Declare the allowed tools Use an existing connector and its approved tools. Find them with `tanomis mcp connector list` and `tanomis mcp connector tools CONNECTOR_ID`. ```yaml title="agent.yaml" integrations: issues: connector: tenant/issue-tracker required: true tools: allow: [search_issues, create_issue] approval_required: [create_issue] ``` `tools.allow` is non-empty and closed by default. Every tool must be approved on the referenced connector. `tools.approval_required` is a subset of that allowlist. Updating a connector cannot add tools beyond the agent's declaration. Neither an MCP scope string nor an egress allowance substitutes for this access. See [Agent manifest](/reference/agent-manifest#governed-mcp-connectors). ## Require a person Each approval-required call creates a permission request for the verified human run initiator. Review the tool and its arguments before answering. The approval is bound to that run and exact call and can be consumed only once. A repeated identical call needs another approval. If the provider fails after consuming an approval, retrying also requires a new approval. The agent cannot answer its own request. Session ownership, the active run, and current grant are still checked. See [Approval-required actions](/guides/run/approval-required-actions). ## Use reviewed schemas The platform automatically snapshots the requested tool descriptions and input schemas, and output schemas when present, during registration. Authors do not write hashes or contract references. Runtime compares served tool definitions against that version's snapshot; changed or unapproved tools are not silently accepted. After a provider schema change, review the changed inventory and deploy a new agent version to capture the updated definitions. The new version still declares its connector explicitly, and existing access disables remain in place. Discovery success alone does not approve inventory or grant agent access. --- Source: /guides/deploy/automation-credentials # Automation credentials Create an organization-owned machine client for CI or a backend, choose its permissions, and rotate its secret. A pipeline or backend uses an **automation client** (also called an M2M client) owned by your organization. An owner or admin creates it; the application uses its client ID and secret. This works the same way on hosted Tanomis and a self-hosted deployment. - An owner or admin creates the client in the Console or with `tanomis m2m create`; the secret is shown once. - A job or service sets `TANOMIS_API_URL`, `TANOMIS_CLIENT_ID`, and `TANOMIS_CLIENT_SECRET`; `tanomis` then runs unattended, and a backend does the same OAuth exchange itself. - Profiles: `observe`, `invoke`, `deploy`. A client reaches every tenant in its organization, including future ones. - Rotation is additive; disable stops access at once, even for an unexpired token. ## Create a client in the Console 1. Open **Settings → Organization → Machine clients**. 2. Choose **Create client**, give it a name, and select a permission profile. 3. Save the **platform origin**, **client ID**, and **client secret** in your CI or service secret manager before closing the dialog. The secret is shown once. For hosted Tanomis the platform origin is `https://api.tanomis.com`. For a self-hosted installation, use your deployment's public API origin. This is an HTTP address, not an OAuth audience identifier. You can also create a client while signed in as an organization owner or admin: ```bash tanomis m2m create production-automation --profile deploy ``` Use `--org ORGANIZATION` if you belong to more than one organization. The output includes `Client:` (the platform record ID used for management), `Client ID:` (the OAuth credential ID), `Client secret:`, and the credential ID and expiry. Store the secret securely; do not send this output to a shared job log. ## Give tanomis three values Inject these variables from your secret manager. The values below are placeholders: ```bash export TANOMIS_API_URL=https://api.tanomis.com export TANOMIS_CLIENT_ID='' export TANOMIS_CLIENT_SECRET='' tanomis agent ls --tenant northwind-sandbox ``` No `tanomis login` step is needed. `tanomis` discovers authentication settings from the platform, exchanges the credentials for an OAuth access token, and keeps the token in memory. It does not save the machine secret in the human login credential store. For a private certificate authority, configure `TANOMIS_CA_BUNDLE` with your CA bundle path. Choose a tenant explicitly for predictable automation. Without one, `tanomis` can select the organization's sole ready tenant or its ready default. A selected tenant controls where a command runs; it does not narrow what the credential can reach. ## Choose permissions | Profile | What it allows | |---|---| | `observe` | Read agents, builds, sessions, operations, tenants, workspaces, and identity. No session file content. | | `invoke` | `observe`, plus run agents, manage session files, and run `tanomis eval ci`. | | `deploy` | `observe`, plus register, build, deploy, promote, and cancel agent builds. | `deploy` does not include `invoke` or access to the evaluation API. `invoke` reaches only the evaluation routes `tanomis eval ci` uses: it reads an agent's checks and datasets, replays a dataset through a version, and compares the results. It cannot change checks, guards, or datasets. Automated registration and deployment are tenant-scoped; this credential cannot publish deployment-wide agents. Organization membership, tenant creation and offboarding, and machine credential management require a signed-in human owner or admin. Every client reaches every active tenant with a ready attachment to its organization, including future tenants. There are no per-tenant grants for machine clients. Use separate organizations when credentials must be isolated between groups of tenants. The profiles supply a standard set of scopes. Owners and admins can also select supported scopes with `tanomis m2m permissions CLIENT --scopes read:agents,read:identity`. Removing a scope or disabling the client takes effect at the next authorization decision, even for an existing token. Adding scopes also requires a newly issued token containing them. ## Rotate and revoke Run lifecycle commands as a human owner or admin. If your shell has machine credentials set, unset `TANOMIS_CLIENT_ID` and `TANOMIS_CLIENT_SECRET` before using your human login. Use the platform record ID from `Client:` or `tanomis m2m ls`, not the display name or OAuth client ID. ```bash tanomis m2m ls tanomis m2m get "$CLIENT" tanomis m2m rotate "$CLIENT" --label rotation --expires-in-days 90 ``` Rotation adds a credential without invalidating the old one. Update your jobs with the new secret, verify they work, then revoke the old credential ID shown by `tanomis m2m get`: ```bash tanomis m2m revoke "$CLIENT" "$OLD_CREDENTIAL" ``` Revocation stops new tokens once credential cleanup completes. Tokens already issued can remain valid until expiry; disable the client if you need to stop access immediately. An organization can have 20 non-deleted clients. Each client can have two live, unexpired credentials. The default credential lifetime is 90 days; the maximum is 365 days. If both slots are occupied, revoke an unused credential before rotating again. A lost create or rotate response cannot be replayed to recover the secret. Inspect the client, revoke the unusable credential, and rotate again. `--request-id` allows safe retries of the same operation, but a replay never returns the secret a second time. ## Change or stop access ```bash tanomis m2m rename "$CLIENT" production-deployer tanomis m2m permissions "$CLIENT" --profile observe tanomis m2m disable "$CLIENT" tanomis m2m enable "$CLIENT" tanomis m2m delete "$CLIENT" ``` Disable is reversible. Delete withdraws access and starts credential cleanup. Neither requires waiting for existing access tokens to expire. Lifecycle operations are attributed to the human who performed them, without recording the secret. Existing `COG_LOGTO_*` CLI configuration remains supported for compatibility. New organization clients should use the three provider-neutral variables above. ## Where to go next - [Deploy from CI](/guides/deploy/deploy-from-ci) - [Authenticate a backend](/guides/run/authenticate-your-backend) - [Organization management API](/reference/organization-management-api) - [M2M CLI reference](/cli/m2m) --- Source: /guides/deploy/deploy-a-version # Deploy a version What tanomis agent deploy does between your source tree and an enabled tenant, and how to read what it tells you. One command takes a local agent directory to a version a tenant can serve. This guide explains each stage so the output makes sense and failures are quick to place. ```bash title="Terminal" tanomis agent deploy ./agents/support-copilot --tenant "$TANOMIS_TENANT" --dry-run tanomis agent deploy ./agents/support-copilot --tenant "$TANOMIS_TENANT" ``` 1. **Preflight** validates the manifest and the lock policy locally. 2. **Package** produces a deterministic bundle. `--dry-run` stops here and prints the plan. 3. **Build** runs remotely and streams progress to your terminal. 4. **Register** records the immutable version with its declared contract. 5. **Warm-start snapshot** is built or reused. 6. **Enable** assigns the version to the tenant you named. This is the only step that routes traffic. ## 1. Preflight Before anything leaves your machine, the CLI: - parses `agent.yaml` and requires a harness (`framework`); - rejects a stray `Dockerfile` or an unsupported top-level `build` field, because the platform owns the image; - flags files that look like secrets (`.env`, `*.pem`, `*.key`, names containing `credentials`) and excludes them from the package; - enforces the Python lock policy: a `uv.lock` or a pinned `requirements.txt`. A bare `pyproject.toml` is rejected. Fix preflight failures in the source tree; nothing was uploaded. ## 2. Package The bundle is deterministic: repackaging the same tree yields the same checksum, which is what lets a version be reproduced and compared later. `--dry-run` ends here and prints what would be built and for which tenant. Use it in CI to validate a change without creating a version. ## 3. Build The bundle is uploaded and built remotely. Progress streams to the terminal. A build failure names the file and step; the version is not registered. ## 4. Register The build result is registered as a new version of the agent, with the manifest contract captured as declared: models, network egress, policy ceiling, connectors, resources, session limits. Registration also validates the declaration against the tenant's ceilings, for example that every `network.egress` entry is inside the tenant's egress ceiling. A rejected declaration names the entry and the ceiling that excluded it. ## 5. Warm-start snapshot For the new version the platform reuses a compatible golden snapshot when one exists, or builds one. This happens before the tenant is enabled, so the first session never races it. If a snapshot cannot be produced, the deploy still succeeds: the version is enabled, sessions start cold until a snapshot exists, and the output says so. > **Availability:** `cr_enabled` requests eligibility for a server-managed golden snapshot. The > server may build one, reuse one, skip it, or not offer the capability. Only the CLI's final golden > message establishes whether sessions warm-start; unavailable, skipped, or failed goldens leave the > version deployed with cold starts. Do not retry a successful deploy. ## 6. Enable The version is enabled for the tenant you passed with `--tenant`. Other tenants are untouched. Enablement is tenant-scoped and reversible: ```bash title="Terminal" tanomis agent get support-copilot --tenant "$TANOMIS_TENANT" # what this tenant serves now tanomis agent versions support-copilot --by-tenant # who serves which version tanomis agent disable support-copilot --tenants "$TANOMIS_TENANT" # stop serving it here ``` `disable` removes only the tenant's enablement and records an audit event. The agent and its versions are untouched, and re-running it is safe. The deploy output ends with the new version label and the tenant it is enabled for. `tanomis agent versions NAME --by-tenant` lists that tenant under the new version. ## Deploy from CI Deploys are non-interactive once the CLI is authenticated. A typical pipeline runs a dry run on every change and a real deploy on merge, then rolls out to customer tenants deliberately with [Roll out and roll back](/guides/deploy/rollout-and-rollback) rather than deploying straight to them. ## Where to go next - [Roll out and roll back](/guides/deploy/rollout-and-rollback): canary a version for named tenants, graduate it, or return to the known-good version. - [Agents and versions](/concepts/agents-and-versions): why versions are immutable and what enablement means. - [`tanomis agent deploy`](/cli/agent/deploy): every flag. --- Source: /guides/deploy/deploy-from-ci # Deploy from CI Validate an agent locally in CI and deploy it to a selected tenant with an organization-owned credential. Create an [automation client](/guides/deploy/automation-credentials) with the `deploy` profile. Store its client ID and secret in your CI secret store, and set the platform API origin as a CI variable. The job needs no interactive login. - **Pull request:** `tanomis agent deploy --dry-run` validates the manifest and lock policy and uploads nothing. - **Merge:** deploy to the sandbox tenant. - **Gate:** the evaluation verdict decides whether a rollout job may run. - **Customers:** `tanomis agent rollout` to named tenants with a canary share, behind an approval. ## Pipeline example This GitHub Actions example uses a runner labeled `tanomis` with `tanomis` and your agent's build and test tools installed. Configure that runner for your repository before using the workflow. Run your agent's own tests in the validation job as well. ```yaml title=".github/workflows/agent.yml" name: support-copilot on: pull_request: paths: ["agents/support-copilot/**"] push: branches: [main] paths: ["agents/support-copilot/**"] jobs: validate: runs-on: [self-hosted, tanomis] steps: - uses: actions/checkout@v4 - run: tanomis agent deploy agents/support-copilot --tenant "$TANOMIS_TENANT" --dry-run env: TANOMIS_TENANT: northwind-sandbox deploy-sandbox: if: github.event_name == 'push' && github.ref == 'refs/heads/main' needs: validate runs-on: [self-hosted, tanomis] environment: sandbox steps: - uses: actions/checkout@v4 - run: tanomis agent deploy agents/support-copilot --tenant "$TANOMIS_TENANT" env: TANOMIS_API_URL: ${{ vars.TANOMIS_API_URL }} TANOMIS_CLIENT_ID: ${{ secrets.TANOMIS_CLIENT_ID }} TANOMIS_CLIENT_SECRET: ${{ secrets.TANOMIS_CLIENT_SECRET }} TANOMIS_TENANT: northwind-sandbox ``` Set `TANOMIS_API_URL` to `https://api.tanomis.com` for hosted Tanomis, or your self-hosted API origin. The tenant IDs are examples; substitute the IDs from your organization. Keep deployment credentials out of pull-request validation jobs, and use a runner policy appropriate for the code you allow to execute. To test a version before anyone is served it, deploy it with `--stage`: it is built, registered, and able to start sessions, and the tenant keeps the version it serves. Then run `tanomis eval ci` against it; see [Evaluate a version](/guides/evaluate/evaluate-a-version). `--dry-run` validates the local package and manifest without API calls or uploads. It does not check live tenant permissions, quotas, or build availability. The authenticated deployment checks those and builds and enables the version in the selected tenant. ## Deploy to a customer tenant Use a protected customer deployment job to deploy the reviewed source revision with `--tenant bluewater-bank`. Configure your CI environment's approval rules before enabling that job. An organization M2M client registers tenant-owned agents, so deployment to another tenant creates or updates that tenant's registration. Do not use `--scope global`, `--tenants all`, or assume a sandbox tenant's version can be promoted into another tenant's registration. The `deploy` profile cannot invoke agents or use the evaluation API. Keep any evaluation or runtime acceptance gate in a separately authorized process; use an `invoke` client when the check needs to run an agent, including `tanomis eval ci`. A deploy credential reaches all ready tenants in its organization, so a CI environment name alone is not a platform permission boundary. After deployment, inspect the enabled version in the target tenant: ```bash tanomis agent versions support-copilot --tenant "$TANOMIS_TENANT" --by-tenant ``` ## Where to go next - [Automation credentials](/guides/deploy/automation-credentials): rotation and immediate disable. - [`tanomis agent deploy`](/cli/agent/deploy): deployment flags and output. - [Customer tenants](/guides/manage/tenants-and-environments) --- Source: /guides/deploy/environments-and-contexts # Environments and contexts Keep a sandbox tenant that serves no customer, reach customers through rollouts, and keep one CLI context per Tanomis environment. On Tanomis your customers are tenants, so "environments" means something specific: a **sandbox tenant** that serves no customer, where developers and the pipeline test, and the customer tenants that a version reaches through a rollout. On your machine, a CLI **context** names which Tanomis environment you talk to and holds its login. - One **sandbox tenant** for testing. Agents, sessions, files, connectors, and policy do not cross tenants, so nothing you do there touches a customer. - Customers are reached by **enabling the same version** for them, a few at a time. Nothing is rebuilt. - One **context** per Tanomis environment on your machine, each with its own login and selected workspace. ## The sandbox tenant and the customer tenants | Tenant | Who deploys | Typical policy | |---|---|---| | `northwind-sandbox` | Developers, from their laptops and pull requests | Wider model allowlist, broad egress ceiling, approvals off for speed | | `bluewater-bank`, `orion-retail`, … | The pipeline or an owner, through a rollout | Each customer's own ceilings, approvals on consequential tools | A tenant's ceilings narrow what a version's manifest declared, so the same version can be admitted in the sandbox and refused for a customer if its egress list exceeds that customer's ceiling. You find out at deploy time, with the entry and the ceiling named. Ask your platform operator to provision the sandbox tenant; see [Customer tenants](/guides/manage/tenants-and-environments). ## Contexts on your machine A context is local configuration: which API environment, which authentication mode, and the login state for it. Most people have one, `saas`, created by `tanomis login`. Add another only for a different Tanomis environment, not for a different tenant. ```bash title="Terminal" tanomis login --console https://api.tanomis.com --name saas tanomis config current-context tanomis config use-context staging tanomis config view ``` Workspace selection is stored per context. Switching context can change the API environment, the cached login, and the selected workspace at once, so check `tanomis workspace show` before a consequential command. ## Switch tenants, not contexts Within one Tanomis environment, moving from the sandbox to a customer is a workspace change: ```bash title="Terminal" tanomis workspace use northwind --tenant northwind-sandbox tanomis agent deploy ./agents/support-copilot --tenant northwind-sandbox tanomis workspace use northwind --tenant bluewater-bank tanomis agent rollout support-copilot --version 0.4.0 --tenants bluewater-bank --canary-share 10 ``` Deploy and session-run commands refuse a `--tenant` that differs from the selected workspace, so a stale selection fails loudly instead of deploying to the wrong customer. ## From the sandbox to every customer Because a version is immutable, reaching customers is enablement, not a rebuild: 1. Deploy to the sandbox. Test with `tanomis session run` and from your application. 2. Run the evaluation gate against the version customers are on. 3. Canary for one or two customers, graduate to the rest, and keep the previous version deprecated for rollback. See [Roll out and roll back](/guides/deploy/rollout-and-rollback) for the customer step and [Deploy from CI](/guides/deploy/deploy-from-ci) for the pipeline shape. ## Where to go next - [Organizations and tenants](/concepts/organizations-and-tenants): how tenants are attached and granted. - [CLI configuration](/reference/cli-configuration): the config file, contexts, and environment variables. --- Source: /guides/deploy/retire-an-agent # Retire an agent Stop serving a version to a tenant, deprecate old versions, and soft-retire an agent while keeping its history. Retiring is three separate, reversible-in-different-ways decisions: stop a tenant serving a version, mark a version as no longer a rollout target, and retire the agent registration itself. None of them deletes evidence. - `tanomis agent disable --tenants T` removes T's enablement. Sessions can no longer start; history stays. - A version that nothing serves can be deprecated; it remains serveable for rollback. - `tanomis agent rm` soft-retires the agent registration and preserves its audit history. ## 1. Stop serving a tenant ```bash title="Terminal" tanomis agent versions support-copilot --by-tenant tanomis agent disable support-copilot --tenants bluewater-bank ``` `disable` removes only the named tenants' enablement and records an audit event. It is idempotent: a tenant that is not enabled is reported and skipped. The agent and its versions are untouched, so re-enabling later is a `rollout`. There is no `all`; removing every tenant's enablement must be explicit. New sessions for that agent are refused in the tenant. Existing sessions keep their history and can be read; whether an in-flight run completes depends on the runtime's state at the moment of the change, so disable between runs where you can. ## 2. Deprecate a version Once no tenant serves a version and you do not intend to roll it out again, deprecate it. A deprecated version is not a target for new enablement but stays serveable for tenants already on it, which is what makes [rollback](/guides/deploy/rollout-and-rollback#6-roll-back-to-the-known-good-version) possible. Deprecation is a status transition; see [Versions and statuses](/guides/deploy/versions-and-statuses). ## 3. Retire the agent ```bash title="Terminal" tanomis agent rm support-copilot --scope global ``` `rm` soft-retires exactly one agent registration. The scope selector is mandatory because global and tenant-scoped agents may share a name; use `--scope tenant --scope-tenant-id TENANT` for a tenant-scoped agent. History, versions, sessions, and ledger entries are preserved. Permanent erasure is an administrator operation and is not reversible. ## Clean up locally ```bash title="Terminal" tanomis agent delete-local support-copilot --agents-root ./agents ``` Removes the scaffolded directory from disk. It does nothing on the platform. ## Where to go next - [Roll out and roll back](/guides/deploy/rollout-and-rollback): moving tenants between versions before you retire one. - [Audit and evidence](/guides/audit/audit-and-evidence): where the disable and retire events land. --- Source: /guides/deploy/rollout-and-rollback # Roll out and roll back Move a global agent version to customers one tenant at a time with a canary, graduate it, and restore the known-good baseline in one command. Move a new version of a global agent into service one customer at a time. The loop is always the same: **inspect → preview → change → verify**. You will canary `support-copilot@v2` for two customer tenants, graduate it to their baseline, and, if the release must be reversed, repoint both to the still-serveable known-good `v1`. - **Inspect** what each tenant serves, **preview** with `--dry-run`, **change** one tenant at a time, **verify** with `versions --by-tenant`. - A canary adds a version at a share while the baseline keeps serving; graduation makes it the baseline. - Rolling back is enabling the still-serveable previous version; nothing is rebuilt and versions are never changed in place. - Command output is provisional. Only the follow-up version view proves what a tenant serves. Before: `acme-eu` and `acme-us` use baseline `v1` with no canary → Canary: both keep baseline `v1` and add `v2` at 10% → Graduated: both use baseline `v2`, and unreferenced `v1` may become deprecated → Rolled back: both use serveable, deprecated `v1` with no canary. Version build and manifest contents stay immutable, while lifecycle status and tenant assignments change separately. ## What each command changes A version's lifecycle status and a tenant's traffic assignment are separate controls. Promotion decides whether a global version may serve; enablement decides where it serves. | Command | What it changes | Tenant traffic | |---|---|---| | `tanomis agent deploy` | Builds and registers an immutable version and, depending on scope, requests enablement. | Follow the deploy result. | | `tanomis agent promote` | Moves a global version to `production` after its gates pass. | None. | | `tanomis agent enable` | Writes a tenant's baseline and optional canary directly. Accepts any serveable version, including deprecated. | The named tenants. | | `tanomis agent rollout` | Promotes the version if needed, then enables it for the named tenants. | Only the named tenants. | | `tanomis agent disable` | Removes the named tenants' enablement; the agent and versions stay. | Stops service there. It is a kill switch, not version rollback. | A **baseline** is the version a tenant normally receives; a **canary** is an optional second version with a tenant-specific share. ## 1. Check access and current assignments You need an authenticated operator context authorized for `read:agents`, `promote:agents`, and every named target tenant, plus an existing global agent, a known-good production version, a candidate version, and explicit tenant IDs. Scopes alone do not grant platform support access to a customer workspace: that path also requires a valid, short-lived support assertion bound to the target customer tenant, obtained through your organization's authorized support workflow. A support assertion is bound to exactly one target tenant. Platform support acting into customer workspaces must run one tenant per command under that tenant's assertion; comma-separated batches are only for an operator whose ordinary authority already covers every named tenant. ```bash title="Terminal" tanomis auth status tanomis agent versions support-copilot --by-tenant ``` `tanomis auth status` confirms the context and login; it does not list granted scopes, tenant grants, or a support assertion, so a permission error can mean any of the three is missing. Confirm that `v1` is `production`, both tenants list `v1` under **BASELINE TENANTS**, and `v2` is the candidate. Record `v1`: that is the rollback target. The Registry may deprecate an older production version once a newer one supersedes it and nothing references it. Built-in `deprecated` is terminal for status transitions but remains serveable: tenants can keep using it, and rollback can repoint tenants to it with `agent enable`; `agent rollout` cannot perform that rollback because it first tries to return every non-production target to `production`. A tenant with no baseline needs one before a canary; canary mode cannot create a tenant's first assignment. ## 2. Preview one tenant's canary Start with one tenant and a 10% share. A canary share must be from 1 through 99; omitting `--canary-share` means a full baseline assignment. ```bash title="Terminal" tanomis agent rollout support-copilot \ --version v2 \ --tenants acme-eu \ --canary-share 10 \ --dry-run ``` ```text dry-run: rollout support-copilot@v2 (would first promote v2 registered -> production) TENANT BEFORE (base/canary) AFTER (base/canary) acme-eu v1 / - v1 / v2 (10%) no changes made (dry-run) ``` Dry run reads the current state and prints the proposed matrix without running promotion gates, promoting, or enabling anything. Stop if the tenant, baseline, candidate, or share differs from your plan. It does not predict application health or move existing sessions. ## 3. Start the canary and verify it ```bash title="Terminal" tanomis agent rollout support-copilot \ --version v2 \ --tenants acme-eu \ --canary-share 10 tanomis agent versions support-copilot --by-tenant ``` Rollout first ensures `v2` is `production`; if a required gate fails, the command stops before enabling any tenant. It then uses the `v1` baseline observed for `acme-eu` when the command began and adds `v2` as the 10% canary. Serialize assignment changes for each tenant: canary does not use a compare-and-set precondition, so a concurrent baseline change after the initial read can be overwritten by the canary's cached baseline. Expect `acme-eu` beside `v1` in **BASELINE TENANTS** and `acme-eu (10%)` beside `v2` in **CANARY TENANTS**. Treat canary output and process exit as provisional: a missing baseline is printed as a skip, but another tenant assignment failure can produce an apparent success line and a zero-failure summary. Only this follow-up version view proves the assignment changed. If it does not match, retry only the tenant that did not change. ## 4. Expand to another named tenant After your own health and outcome checks pass, preview and apply the same candidate and share for the next tenant, then verify: ```bash title="Terminal" tanomis agent rollout support-copilot \ --version v2 \ --tenants acme-us \ --canary-share 10 \ --dry-run tanomis agent rollout support-copilot \ --version v2 \ --tenants acme-us \ --canary-share 10 tanomis agent versions support-copilot --by-tenant ``` The share belongs to each tenant's assignment; it is not a global split across the tenant list. Keep naming tenants explicitly as the rollout expands. ## 5. Graduate the candidate When the canary meets your release criteria, preview a baseline rollout for the same tenants without `--canary-share`. The batch form assumes ordinary operator authority over both tenants; support act-as previews and applies each tenant separately. ```bash title="Terminal" tanomis agent rollout support-copilot \ --version v2 \ --tenants acme-eu,acme-us \ --dry-run ``` The **AFTER** cells must read `v2 / -`. Then apply and verify: ```bash title="Terminal" tanomis agent rollout support-copilot \ --version v2 \ --tenants acme-eu,acme-us tanomis agent versions support-copilot --by-tenant ``` For each successful tenant, the plain rollout makes `v2` the 100% baseline and clears its canary. A partially failed baseline rollout returns an error, but still read per-tenant results and the version view before declaring it complete. If these were the last `v1` assignments, expect `v1` to become `deprecated`: superseded, not deleted or unservable. ## 6. Roll back to the known-good version Rollback repoints enablement to the serveable version recorded in step 1; it is not a lifecycle transition. Re-check that `v1` is `production` or built-in `deprecated`. `agent enable` has no dry-run mode, so write the preview down first: ```text TENANT BEFORE (base/canary) AFTER (base/canary) acme-eu v2 / - v1 / - acme-us v2 / - v1 / - ``` ```bash title="Terminal" tanomis agent enable support-copilot \ --tenants acme-eu,acme-us \ --baseline v1 tanomis agent versions support-copilot --by-tenant ``` Each successful tenant now serves `v1` with no canary, even while `v1` is `deprecated`; the candidate remains an immutable version. Like canary mode, `agent enable` prints per-tenant failures without a failing exit for a partial result, so require a summary with zero failed tenants and verify both under the `v1` baseline. Do not use `tanomis agent disable` for this: it removes service rather than returning a tenant to a known-good version. ## Interpret failures before retrying | Signal | What changed | Next action | |---|---|---| | Permission denied | Nothing. The scope, tenant grant, support assertion, or its tenant binding may be missing. | Have an administrator confirm all of them, then retry. | | Version not found | Nothing. | Re-run the version view and use an exact version label. | | Required promotion gate failed | The candidate did not reach `production`; rollout stopped before enablement. | Resolve the gate, then repeat the dry run. | | Known-good version is built-in `deprecated` | It is serveable, but `rollout` cannot transition it back. | Use named-tenant `agent enable --baseline`. | | Known-good version is not serveable | The Registry rejects the enablement. | Build the known-good source as a new immutable version. | | `cannot canary — tenant has no baseline` | That tenant was skipped; canary mode may still exit successfully. | Assign a baseline first, or drop the tenant from the list. | | Canary output looks successful but the version view disagrees | The tenant did not change. | Retry only that tenant. | | Another operator changed the baseline during a canary | Canary may have restored the baseline it observed at start. | Serialize assignment changes; repeat the preview. | | Baseline rollout reports a partial failure | Successful tenants changed; a failed tenant's final assignment may be unchanged or may have committed. | Read the version view before retrying. | | Rollback enable reports failed tenants | Process exit may still be successful, and a failed tenant's final assignment is unknown until read back. | Read the version view, then re-enable the failed tenants. | The safe stopping point is always explicit state: the named tenants under the intended baseline and canary columns in `tanomis agent versions support-copilot --by-tenant`. ## Where to go next - [Quickstart](/getting-started/quickstart): the first deploy. - [Deploy from CI](/guides/deploy/deploy-from-ci): the rollout job behind an approval. - [Journey commands](/cli#roll-out-and-roll-back): compact command lookup. --- Source: /guides/deploy/versions-and-statuses # Versions and statuses Version labels, the status lifecycle from draft to retired, and how status differs from the enablement that routes traffic. Every deploy creates a version. A version has a label you choose, a status the platform moves it through, and, separately, the tenants enabled for it. Knowing which of the three you are changing keeps rollouts predictable. - **Label** is the `version` in `agent.yaml`, such as `0.4.0` or `v2`. It is yours; it does not have to be sequential. - **Status** is the lifecycle: `draft` → `production`, with `rejected`, `deprecated`, and `retired` as side exits. Status gates whether a tenant may serve the version. - **Enablement** is which tenants actually serve it. Status alone routes nothing. ## Statuses | Status | Meaning | Can serve traffic | |---|---|---| | `draft` | Registered and built, not yet promoted. The default after `tanomis agent deploy` registers the version, before enablement. | No | | `production` | Promoted. Tenants may be enabled for it. | Yes, where enabled | | `rejected` | Reviewed and refused. It never serves. A rejected first version does not pin the agent's identity mode. | No | | `deprecated` | Still serveable for tenants already on it, so a rollback can return to it, but not a target for new enablement. | Yes, where already enabled | | `retired` | Withdrawn from service. History is preserved. | No | `tanomis agent statuses` prints the lifecycle your environment enforces, including the transitions each status allows and any operator-added custom statuses. Trust it over this table if they differ. ```bash title="Terminal" tanomis agent statuses tanomis agent versions support-copilot --tenant "$TANOMIS_TENANT" ``` ## Status is not traffic | Command | Changes status | Changes enablement | |---|---|---| | `tanomis agent deploy --tenant T` | Registers a draft, promotes it as part of the deploy | Enables T for the new version | | `tanomis agent promote --version V` | `draft` → `production` | No | | `tanomis agent enable --version V --tenants A,B` | No | Enables A and B | | `tanomis agent rollout --version V --tenants A` | Promotes if needed | Enables A, as baseline or canary | | `tanomis agent disable --tenants A` | No | Removes A's enablement | Promotion changes the global status only. If you want a tenant to serve a version, enable it for that tenant, or use `rollout`, which does both for exactly the tenants you name. ## Labels The label comes from `version` in the manifest. Use a scheme your team can read in a rollout table: semantic versions when your agent has an API to other systems, dated labels when it does not. The platform identifies versions by an immutable id underneath; the label is for people. Deploying with a label that already exists on the agent registers a new immutable version with the same label. Avoid it: it makes `versions --by-tenant` ambiguous to read. ## Where to go next - [Deploy a version](/guides/deploy/deploy-a-version): the stages of a deploy. - [Roll out and roll back](/guides/deploy/rollout-and-rollback): canary, graduate, return. - [Retire an agent](/guides/deploy/retire-an-agent): removing enablement and the agent itself. --- Source: /guides/evaluate/capture-traces # Capture execution traces What a run's execution trace contains, the three content capture levels, and who can read a trace. Every run emits one execution trace on the tenant's own trace plane, separate from the platform's operational telemetry and separate from the audit ledger. The trace is what evaluators score and what an engineer opens to see why a run did what it did. - One trace per run. The session groups the run's turns; the root span is the agent invocation, with model calls and tool calls as children. - **Content capture** is a tenant policy with three levels: reference, redacted, full. - The capture decision is made inside the sandbox, where the content is; the read path rechecks the tenant's current policy before serving it. - Traces are tenant-isolated and never copied into the audit ledger. ## What a trace contains | Span | What it records | |---|---| | Agent invocation (root) | The run, its version, the session, timing, and the outcome | | Model call | The model, token usage, latency, and content at the captured level | | Tool call | The tool, its arguments and result at the captured level, and the policy decision | Every span carries the run and session identifiers, so a trace can be matched to the audit ledger entry for the same run. Spans follow OpenTelemetry's generative-AI conventions, which is what makes [exporting them to your own backend](/guides/evaluate/export-traces-opentelemetry) a planned option rather than a rewrite. ## Capture levels | Level | What is stored | |---|---| | `reference` | Structure and hashes only: which calls happened, in what order, with what outcome. No prompt or payload bodies. | | `redacted` | Bodies with sensitive spans removed by the tenant's redaction rules. | | `full` | Complete bodies. Serving full content is gated by an approval, not just the policy. | The level is a tenant setting. An agent cannot raise it: the capture decision is resolved inside the sandbox from the tenant's policy, and the platform's relay treats span attributes as pass-through data, so an agent-provided label cannot promote content into a class it is not allowed to be. ## Who can read a trace Trace reads are tenant-scoped and re-check the tenant's current policy at read time. The content class served comes from the server-recognized slot, not from anything the agent wrote. Owners and admins can see which level their tenant runs in the Agents screen under trace capture. ## Where to go next - [Evaluation overview](/guides/evaluate/overview): how traces become assessments and verdicts. - [Audit and evidence](/guides/audit/audit-and-evidence): the ledger that references these traces by hash. - [Data handling and retention](/guides/govern/data-handling): planned page on storage and retention. --- Source: /guides/evaluate/datasets-and-experiments # Datasets and experiments Curate versioned datasets of examples and run experiments that grade a whole dataset and roll up metrics. A dataset is a versioned set of examples with expected outcomes. An experiment grades a candidate against a dataset version and produces aggregate metrics you can compare side by side. This page is planned and not yet written, because the capability it describes is not yet generally available. The structure is here so you can see where it fits; the notice comes off when it ships. ## What this page will cover - Create a dataset from production traces or from a file. - Version a dataset and reference it from a target. - Run an experiment and read its aggregate metrics. - Compare experiments side by side. ## Meanwhile - [Evaluate a version](/guides/evaluate/evaluate-a-version) already covers pinning a target to a dataset version and comparing with a baseline. --- Source: /guides/evaluate/evaluate-a-version # Evaluate a version Test a candidate version against the version serving a tenant on real past runs, and post the result on the pull request. Before a version serves real traffic, replay real conversations through it and through the version you already trust, grade both with the agent's checks, and see what got worse. One command does it, so it drops into the pipeline that builds the agent. > **Availability:** Evaluation is enabled per tenant. See [What is available where](/reference/availability). - **Checks:** the agent's checks (Quality > Checks) grade both versions, the same checks that grade its live runs. - **Dataset:** runs you picked in the Console with **Add to dataset**. The replay reads each run's first message again; nothing from the conversation is copied into the dataset. - **One command:** `tanomis eval ci` replays the dataset through both versions at once, compares them, and replays what got worse once more before calling the candidate worse. - **Result:** a Markdown summary for the pull request, and an exit code the pipeline can act on. ## Before you start - The agent has checks. `tanomis eval ci` grades with them and refuses to run without them. - A dataset of the agent's runs. On the Quality screen, open a run and choose **Add to dataset**; collect the conversations you want every new version to keep handling well. - The candidate version is built and registered in the tenant. Stage it so nobody is served it yet: ```bash title="Terminal" tanomis agent deploy agents/support-copilot --stage ``` This stages the version in `agent.yaml`, say `0.4.0`. A staged version can start sessions, so it can be replayed, but the tenant keeps serving the version it served. Release it within 14 days with `tanomis agent promote support-copilot --version 0.4.0 --scope-tenant TENANT`; a staged version nobody releases is then rejected and its storage reclaimed. ## Run it ```bash title="Terminal" tanomis eval ci support-copilot --dataset refunds --candidate 0.4.0 --summary summary.md ``` The baseline is the version serving the tenant; name another with `--baseline`. Pin a dataset version with `--dataset refunds@3`, otherwise the latest is used. `--threshold 0.05` lets up to 5% of the comparable examples get worse before the test fails; the default is none. The command prints the summary and exits with: | Exit code | Meaning | | --- | --- | | `0` | No worse than the baseline. | | `1` | Worse: some examples that passed on the baseline fail on the candidate. | | `2` | Not enough to tell, for example when too few examples could be replayed. | | `3` | The test could not run (no checks, unknown dataset or version, lost connection). | An answer of "worse" is checked before it is given. Each example that passed on the baseline and failed on the candidate is replayed once more on the candidate; it counts only if it fails again. One that passes on the second try is listed as flaky and not counted, and the verdict is worked out again from the examples that failed twice. Turn this off with `--confirm=false`. The summary lists each check's pass rate on both versions, each example that got worse with a link to its run in the Console, and, separately, the flaky ones. The Console's address is taken from the API's (`api.example.com` gives `console.example.com`); name it with `--console-url` if yours differs. When it cannot be worked out, the summary shows trace ids instead. Each example runs through the production run path on both versions, with the tenant's real connections: a tool call a replay makes really happens. Test in a tenant whose connections point at test systems, such as your sandbox tenant, and build the dataset from that tenant's runs. A replay runs with nobody there. If a version stops to ask a person, for example because a rule holds a shell command for approval, the replay is stopped at once, nothing is left in your approvals inbox, and the summary counts that example as one that could not be replayed and says what it asked for. A run that had attached files cannot be replayed either. ## In a pull request The job stages the pull request's version in the sandbox tenant that holds the dataset, then tests it against the version that tenant serves. Staging changes nothing anyone is served; the replays themselves are real runs (see above). It uses two [automation clients](/guides/deploy/automation-credentials): a `deploy` client to stage the version, and an `invoke` client for the test (`invoke` can replay and grade; it cannot deploy). A deploy client could also release a version, so run the job only for pull requests from branches of your own repository, in a protected environment. It runs on a GitHub-hosted runner, which already has `yq` and `gh`; the first step installs the `tanomis` CLI, pinned to a release so a new CLI version cannot change the job under you. It posts the summary on the pull request: ```yaml title=".github/workflows/agent-eval.yml" name: support-copilot eval on: pull_request: paths: ["agents/support-copilot/**"] jobs: eval: if: github.event.pull_request.head.repo.full_name == github.repository runs-on: ubuntu-latest environment: eval permissions: pull-requests: write env: TANOMIS_API_URL: ${{ vars.TANOMIS_API_URL }} TANOMIS_TENANT: northwind-sandbox AGENT: support-copilot CANDIDATE: 0.0.0-pr${{ github.event.pull_request.number }}.${{ github.run_id }} steps: - uses: actions/checkout@v4 - name: Install tanomis run: | curl -fsSL https://get.tanomis.com/install.sh | TANOMIS_VERSION=v0.6.0 TANOMIS_INSTALL_DIR="$HOME/.local/bin" sh echo "$HOME/.local/bin" >> "$GITHUB_PATH" - name: Stage the candidate run: | yq -i '.version = env(CANDIDATE)' "agents/$AGENT/agent.yaml" tanomis agent deploy "agents/$AGENT" --stage --no-stream-logs env: TANOMIS_CLIENT_ID: ${{ secrets.TANOMIS_DEPLOY_CLIENT_ID }} TANOMIS_CLIENT_SECRET: ${{ secrets.TANOMIS_DEPLOY_CLIENT_SECRET }} - name: Test the candidate id: eval run: | set +e tanomis eval ci "$AGENT" --dataset refunds --candidate "$CANDIDATE" --summary summary.md echo "code=$?" >> "$GITHUB_OUTPUT" env: TANOMIS_CLIENT_ID: ${{ secrets.TANOMIS_EVAL_CLIENT_ID }} TANOMIS_CLIENT_SECRET: ${{ secrets.TANOMIS_EVAL_CLIENT_SECRET }} - name: Comment on the pull request if: always() && hashFiles('summary.md') != '' run: gh pr comment "${{ github.event.pull_request.number }}" --body-file summary.md env: GH_TOKEN: ${{ github.token }} - name: Fail unless the candidate passed if: steps.eval.outputs.code != '0' run: exit ${{ steps.eval.outputs.code }} ``` The last step fails the check for any exit code other than `0`: worse (`1`), not enough to tell (`2`), or could not run (`3`). Each run stages a new version under its own label; staged versions serve nobody, and one not released within 14 days is rejected and its storage reclaimed. The version you release is built from the merged source by your deploy pipeline (see [Deploy from CI](/guides/deploy/deploy-from-ci)). The test replays each example on both versions at the same time, so it takes minutes, not seconds. `--timeout` bounds it (45 minutes by default), and a machine client's token is renewed while it waits. The baseline is replayed only when needed. If the same baseline version was graded with the same checks on the same dataset version within the last 7 days, that result is reused and only the candidate is replayed; the summary says so and when the baseline was graded. `--baseline-max-age` changes the 7 days (`--baseline-max-age 0` always replays the baseline), and `--baseline-experiment` names a finished baseline experiment to reuse; it must have been graded with the same checks on the same dataset version. ## Step by step `tanomis eval ci` is built from commands you can also run yourself: ```bash title="Terminal" tanomis eval target create --agent-version VERSION_ID --dataset-version DATASET_VERSION_ID tanomis eval target run TARGET_ID --max-examples 50 tanomis eval compare BASELINE_EXPERIMENT --to CANDIDATE_EXPERIMENT --threshold 0.02 ``` A target pins one version to one dataset version; running it replays the examples through the production run path. `tanomis eval compare` exits non-zero unless the verdict is `pass`. The Evaluations screen in the Console lists each experiment's runs with their results, cost, and latency, and opens the trace behind each one. ## Where to go next - [Roll out and roll back](/guides/deploy/rollout-and-rollback): what to do once the candidate passes. - [Deploy from CI](/guides/deploy/deploy-from-ci): the deploy half of the pipeline. --- Source: /guides/evaluate/export-traces-opentelemetry # Export agent traces with OpenTelemetry Send each run's execution trace to your own observability backend over OTLP, with the content controls your tenant policy allows. Every run already produces one OpenTelemetry trace on the tenant's trace plane. Exporting it to your own backend over OTLP is planned. This page is planned and not yet written, because the capability it describes is not yet generally available. The structure is here so you can see where it fits; the notice comes off when it ships. ## What this page will cover - Configure an OTLP endpoint and credentials for the tenant. - Which spans and attributes are exported, and how content capture levels apply. - Correlating exported traces with the audit trail by run and session id. - Sampling and cost. ## Meanwhile - [Capture execution traces](/guides/evaluate/capture-traces) explains what a trace contains today. --- Source: /guides/evaluate/human-feedback # Human feedback and annotation Record human judgements beside automated assessments and use them to calibrate scorers. Automated scores are only as good as their calibration. Human feedback on an assessment is stored beside it and feeds calibration. This page is planned and not yet written, because the capability it describes is not yet generally available. The structure is here so you can see where it fits; the notice comes off when it ships. ## What this page will cover - Annotate an assessment in the Console. - Feedback through the API. - Using feedback to calibrate a judge. ## Meanwhile - [Evaluate a version](/guides/evaluate/evaluate-a-version) shows the Evaluations screen where assessments appear. --- Source: /guides/evaluate/online-monitoring # Online monitoring Score a sample of production runs continuously and alert on drift. Online evaluation samples live runs, scores them with the same scorers used offline, and watches the trend so a regression shows up before a customer reports it. This page is planned and not yet written, because the capability it describes is not yet generally available. The structure is here so you can see where it fits; the notice comes off when it ships. ## What this page will cover - Sampling policy and cost controls. - Monitors and thresholds. - Where alerts land. ## Meanwhile - [Audit and evidence](/guides/audit/audit-and-evidence) explains the execution trace these scores are computed from. --- Source: /guides/evaluate/overview # Evaluation overview How Tanomis turns a run into a trace, a trace into an assessment, assessments into experiments, and a verdict into a release gate. Evaluation on Tanomis is built on the run you already have. Every run produces an execution trace; evaluators score traces into assessments; a set of assessments over a dataset is an experiment; and a comparison between experiments becomes a verdict that can gate a rollout. > **Availability:** Evaluation is enabled per tenant, shadow-first. See [What is available where](/reference/availability). - **Trace**: one OpenTelemetry trace per run, on the tenant's own trace plane, with content captured at the level tenant policy allows. - **Assessment**: a scorer's verdict on one trace, with a reason and the evidence it used. - **Experiment**: a candidate version replayed over a dataset version, with aggregate metrics. - **Gate**: a candidate-versus-baseline comparison whose verdict can block promotion. ## The flow | Step | What it is | Where you see it | |---|---|---| | Run | Any run, from the Console, the CLI, or your application | Sessions | | Trace | The run's model calls, tool calls, timing, and captured content | Evidence, Evaluations | | Assessment | A scorer's verdict on a trace | Evaluations | | Experiment | A candidate replayed over a dataset, with rolled-up metrics | Evaluations | | Verdict | Candidate compared with baseline against a threshold | `tanomis eval compare`, the promotion gate | Nothing in this flow copies content into the audit ledger. The ledger records that a gate decision was made and references the trace content by hash; the trace store holds the content under its own capture, retention, and access rules. See [Audit and evidence](/guides/audit/audit-and-evidence). ## Offline and online **Offline** evaluation scores a candidate before it serves traffic: pin a target, replay a dataset, compare with the baseline. This is the path in [Evaluate a version](/guides/evaluate/evaluate-a-version). **Online** evaluation samples live runs and scores them continuously so drift shows up early. It is planned; see [Online monitoring](/guides/evaluate/online-monitoring). ## What decides a score Scorers run over traces. Deterministic scorers check structure and outcomes; judge-model scorers ask a model, through the same gateway agents use and under an allowlist your administrator controls. Human feedback recorded beside an assessment is how a judge is calibrated before a gate is allowed to enforce. ## Where to go next - [Capture execution traces](/guides/evaluate/capture-traces): what a trace contains and how much content it carries. - [Evaluate a version](/guides/evaluate/evaluate-a-version): the offline path, step by step. - [Datasets and experiments](/guides/evaluate/datasets-and-experiments) and [Scorers and judge models](/guides/evaluate/scorers-and-judges): planned pages for the workbench. --- Source: /guides/evaluate/scorers-and-judges # Scorers and judge models Choose built-in scorers, configure a judge model under the tenant allowlist, and publish a tenant-authored scorer. A scorer turns a trace into an assessment. Some are deterministic; some use a judge model, which calls through the same gateway as agents and under an allowlist your administrator controls. This page is planned and not yet written, because the capability it describes is not yet generally available. The structure is here so you can see where it fits; the notice comes off when it ships. ## What this page will cover - The built-in scorer library. - Configuring a judge model and its allowlist. - Publishing a tenant-authored scorer without shipping code. - Calibrating a judge before a gate enforces. ## Meanwhile - [Evaluation overview](/guides/evaluate/overview) explains where scorers sit in the flow. --- Source: /guides/govern/data-handling # Data handling and retention What Tanomis stores about your sessions, runs, files, and traces, for how long, and who can read it. Tanomis keeps two records with different retention and access rules, plus session workspaces and files. The exact retention periods and encryption options are being confirmed for publication. This page is planned and not yet written, because the capability it describes is not yet generally available. The structure is here so you can see where it fits; the notice comes off when it ships. ## What this page will cover - Where prompts, tool payloads, files, and traces are stored. - Retention periods and deletion. - Content capture levels and per-tenant keys. - Regional residency. ## Meanwhile - [Audit and evidence](/guides/audit/audit-and-evidence) explains the two evidence planes today. --- Source: /guides/govern/restrict-network-egress # Restrict network egress Declare the external hosts an agent may reach, understand the tenant ceiling that bounds it, and read a refusal. By default an agent can reach the platform's own services and nothing else. To let it call an external API, declare the host in the manifest. The declaration is part of the version's contract: validated at deploy, frozen for the version, and enforced on every connection at run time. ```yaml title="agent.yaml" network: egress: - github.com - api.example.com ``` > **Availability:** Author-declared egress is enabled per tenant; without it the list is validated and recorded but no external route exists. See [What is available where](/reference/availability). - Entries are bare hostnames. An entry covers its subdomains. - An administrator's per-tenant ceiling only narrows your list; it never adds to it. - A declaration outside the ceiling fails the deploy in seconds, naming the entry and the ceiling. - Platform destinations, such as the LLM gateway, are added for you and are not declarable. ## Allow arbitrary public websites For an agent that needs to browse websites it cannot enumerate in advance, use: ```yaml title="agent.yaml" network: egress_mode: public ``` Omit `egress`, or leave it empty. A non-empty hostname list combined with `public` is rejected. Without `egress_mode`, the existing hostname-list behavior applies; `allowlist` can also be explicit. An unset tenant ceiling permits public mode. Any configured hostname ceiling, including an empty one, rejects it at deploy and session admission. Public-mode `cog agent deploy --dry-run` performs an authenticated read-only ceiling validation; it creates no build and uploads no source. Public means any canonical public **hostname through the managed proxy**. Private, metadata, reserved and platform infrastructure addresses remain blocked, including when public DNS points to them. It does not enable raw IP destinations, direct sockets, UDP or QUIC. Clients must use the proxy; transport and port limits still apply. Platform credentials and other policy controls remain unchanged. **Availability:** Public mode requires runtime-api, cr-node and cr-helper versions supporting the versioned egress policy, plus an enabled external route and trusted resolver. An older receiver refuses activation. Deploy the supporting platform release before deploying a public-mode agent. The declaration is immutable per agent version. Ceiling tightening blocks new sessions and resumes; it does not close an active connection. Stop affected sessions for immediate revocation, then start new sessions under the updated ceiling. Session responses expose `external_egress_policy`, and connection ledger records identify the mode and ceiling revision in their policy ID. ## Write a valid entry Each entry is a lowercase hostname with at least two labels, no scheme, port, path, or trailing dot. Anything else is refused at deploy rather than rewritten, so what you wrote is what is enforced. | Not allowed | Why | |---|---| | `*.github.com` | No wildcards. `github.com` already covers `api.github.com`. | | `https://github.com`, `github.com:443`, `github.com/path` | The match is on host alone. | | `140.82.121.4` | An address has no DNS identity; the allowlist matches the host the agent asks for. | | loopback or in-cluster names | Names for the sandbox itself or the platform's own network are never declarable. | | `GitHub.com`, `github.com.` | Not canonical. Write `github.com`. | Declare the narrowest name that covers what you need. `github.com` admits `api.github.com`; it does not admit `notgithub.com`. ## The tenant ceiling Your administrator can set a ceiling for the tenant. Your declaration is intersected with it: - A ceiling of `github.com` admits a declaration of `api.github.com`, because that is narrower. - A declaration of `example.com` under a ceiling that does not include it fails the deploy with the entry and the ceiling named, before any build is created. - An unconfigured tenant admits any public hostname, so an unset ceiling behaves as though only your manifest applied. ## What is enforced, and where - **At deploy**: the grammar and the tenant ceiling, before a build exists. - **At run time**: the managed proxy admits the platform's destinations plus your declared hosts, or public hostnames when `egress_mode: public` is selected. Every external destination must resolve to a checked public address. A refused connection fails at the agent with the host named. ## Change the list The list is immutable for a version. To add or remove a host, change the manifest and deploy a new version, then roll it out. See [Deploy a version](/guides/deploy/deploy-a-version). ## Where to go next - [Policy, approvals, and evidence](/concepts/governance): how egress fits with the other ceilings. - [Agent manifest](/reference/agent-manifest#network): the field reference. --- Source: /guides/govern/set-the-authorization-ceiling # Set the authorization ceiling Declare the most authority an agent's own tokens can ever carry, in the three scope grammars the platform accepts, and understand what the ceiling does and does not govern. `policy.scope_ceiling` is the upper bound on what an agent may do acting as itself. Anything that mints authority for the agent intersects the request with this ceiling; nothing widens it. ```yaml title="agent.yaml" policy: scope_ceiling: - "llm:your-model-family:invoke" - "mcp:github:create_issue:write" - "calendar.read" allowed_audiences: - "https://your-workspace-api.example" ``` - Three grammars, no wildcards: `.`, `mcp:::`, `llm::`. - Required on a hosted control plane. Where it is optional, an absent ceiling means allow-all with a warning. - It governs **delegation** between agents and any future scoped token exchange. It does not govern the model allowlist or connector tool allowlists; those have their own controls. - Changing it is a new version. The broker reads the registered contract with a short cache, on a separate clock from traffic rollout. ## Write the entries | Form | Example | Covers | |---|---|---| | `.` | `calendar.read` | A capability on a named resource | | `mcp:::` | `mcp:github:create_issue:write` | One tool on one connector, with a verb | | `llm::` | `llm:your-model-family:invoke` | Calling a model family | Every entry must match exactly one grammar. Wildcards are rejected at validation, so you cannot write `mcp:github:*`. Keep the list to what the agent actually does: a ceiling is the maximum, and every entry is authority the agent could be granted. When you change `model`, update the matching `llm::invoke` entry, or the declared ceiling stops describing the model the agent calls. ## What the ceiling governs - **Agent-to-agent delegation.** On a delegated call the caller's ceiling is intersected with the callee's, and only scopes inside both are granted. An agent with no ceiling cannot receive a delegated grant at all. - **Token exchange.** If a supported flow exchanges a token against one of the agent's `allowed_audiences`, a requested scope outside the ceiling is refused. No public runtime workflow exposes that exchange today; the contract is held. ## What it does not govern | Concern | Where it is actually enforced | |---|---| | Which model a run may call | `permitted_models`, at the LLM gateway | | Which integration tools an agent may use | `integrations..tools.allow`, at the connector gateway | | Which actions pause for a person | `policy.approval_required_tools` and connector `approval_required_tools` | | Which external hosts the agent may reach | `network.egress` and the tenant ceiling, at the egress proxy | Adding an `mcp:...` scope does not approve a connector tool, and adding an `llm:...` scope does not replace the model allowlist. Sizing the ceiling for those will not work. ## Change it safely The ceiling is part of the version's contract, so changing it means a new version. Two clocks apply: traffic follows enablement, while the authorization contract is read from the agent's registered manifest through a short in-process cache. A registration can change the contract before the new version serves traffic, and rolling traffic back does not necessarily restore the earlier contract. Review authorization impact at registration time and verify the effective contract after a change. ## Where to go next - [Identity and access](/concepts/identity-and-access): the identity modes and why authority only narrows. - [Delegate work to another agent](/guides/run/delegate-to-another-agent): the ceiling in use. - [Agent manifest](/reference/agent-manifest#authorization-contract): the field reference. --- Source: /guides/govern/support-access # Support access and break-glass How Tanomis support can act inside your workspace, what a support grant contains, and where you see it. Tanomis support sometimes needs to act inside a customer workspace: to help during onboarding, diagnose a stuck provisioning, or reproduce an issue. That access is never a standing platform credential. It is a short-lived, signed grant scoped to one workspace, with a reason, an expiry, and an audit binding. - Support acts through a **support grant**: one workspace, one reason, a short expiry, signed by the platform. - The grant is the only way support reaches a customer workspace; a raw platform credential does not. - Every action taken under a grant lands in your organization's audit trail, attributed to the support principal and the grant. - You can read the trail at any time; ask your administrator to revoke a grant early. ## What a support grant is A grant names the target workspace, the support principal, the reason given, and when it expires. The platform signs it, and every service on the path to your data checks it, so the grant cannot be widened to another tenant or extended by the holder. Without a grant, the platform's own administrators have no route into a customer workspace's sessions, files, or evidence. ## What you see Actions under a grant appear in the audit trail like any other action, with the support principal as the actor and the grant as the authority. In the Console, the Assurance section and `tanomis org audit` show them alongside your members' actions. ```bash title="Terminal" tanomis org audit --limit 200 ``` ## Asking for help When you open a support request that needs someone in your workspace, support asks for the workspace and states the reason; the grant records both. Revocation is immediate and also recorded. ## Where to go next - [Audit and evidence](/guides/audit/audit-and-evidence): reading the trail. - [Organizations and tenants](/concepts/organizations-and-tenants): how access is decided for everyone else. --- Source: /guides/govern/usage-and-spend # Usage and spend How model usage is metered per run, where you read it, and how a session budget bounds what an agent can spend. Every model call goes through the tenant's LLM gateway, which meters it against the run that made it. Usage therefore rolls up from runs to sessions, agents, and the principals who started them, and a session can carry a budget that bounds it. > **Availability:** Metering and budgets are enabled per tenant; a session shows usage as not measured where they are off. See [What is available where](/reference/availability). - Usage is recorded per run: input tokens, output tokens, and the model. - The Console's session view shows usage by run and, where a budget governs the session, the cap, what is reserved, what has settled, and what remains. - A run that would exceed the session's budget is refused before it starts, not cut off midway. ## Read usage Open a session in the Console and choose the tokens panel. It shows totals for the session and a breakdown by run, split into input and output. The same numbers are attributed in the execution trace's model-call spans, so an evaluation experiment can report cost beside quality. ## Budgets Where a session budget is set, the panel shows: | Figure | Meaning | |---|---| | Cap | The session's budget | | Reserved | Usage held for a run that is still executing | | Settled | Usage recorded for completed runs | | Available | Cap minus reserved minus settled | A run is admitted only if its reservation fits. "No budget governs this session" means the session has no cap, and usage is metered but unbounded. ## Where spend is decided Spend is a consequence of three declarations you already control: the models in `permitted_models`, the tools the agent may call, and how long sessions live. A cheaper model in the allowlist that your application selects for routine requests is the largest lever. See [Choose models](/guides/build/choose-models). ## Where to go next - [Sessions and runs](/concepts/sessions-and-runs) - [Evaluate a version](/guides/evaluate/evaluate-a-version): cost beside quality for a candidate. --- Source: /guides/govern/write-tenant-policy # Write tenant policy Add a rule that limits what agents on a tenant may do, see what it would have done, and publish it as the next version. A tenant rule is a limit on top of every agent's manifest. The manifest is the ceiling each agent declared for itself (models, hosts, tools); a rule is the tenant saying "and also not this", for every agent, for named agents, or for agents in a lifecycle status. Rules live in the Console under **Policies**. - A rule is a sentence: **who** it applies to, **what** it watches (model calls, tool calls, or prompt content, external hosts, or managed shell commands), and whether a match blocks or requires human approval. Hard blocks take precedence. - The policy is published as **versions**. Editing never touches what is live: edits go into one pending change, kept as you type, and **Review and publish** makes them the next version, all together or not at all. **Versions** rolls back and restores. - Whether a refusal is applied or only recorded is the tenant's posture, the **Observing / Enforcing** switch at the top of Rules, with the week's number beside it. - Workspace administrators can publish. Workspace members can read, prepare a change, try it, and request a publish. ## Add a rule 1. Open **Policies** and choose **New rule**: start from a template (block specific models, block a tool for some agents, keep secrets out of prompts), write it as code, or upload a policy file. 2. The rule opens on its own screen. Name it: the name is what Activity and the ledger show when it fires. 3. **Applies to**: all agents, named agents, or agents in a lifecycle status such as `experimental`. Named agents and a status combine. 4. **Watch**: model calls or tool calls (name the ids, or anything outside the agent's manifest), or prompt content (terms, patterns, a length cap, the secrets detector). 5. The **Form** and **Code** tabs are two views of the same rule. Anything the form cannot say stays as code; the list still shows the sentence it can read. 6. **What it would do**, beside the form, replays the last seven days against the rule as you edit, and **Try a request** judges one request for a named agent (prompt-content rules run the pasted text through the real inspector). 7. Edits are kept as you type. **Back to rules** never loses work. ### When a prompt-content rule matches A message someone sends is checked before its run starts. A match refuses the send and names the rule; nothing reaches the agent, and the session stays usable for the next message. Text the agent reads on its own, such as tool results, is checked when it goes to the model, and a match ends that turn with a short refusal in place of the model's reply. Only new text is checked, so a published rule applies from then on and does not re-judge a session's earlier messages. ## Review and publish The bar at the bottom of Rules collects your change. **Review and publish** opens a sheet with every changed rule as a before-and-after sentence, the whole new policy replayed over last week (and a note when it blocks fewer requests than today's), and a summary line. **Publish** makes it the next version: every engine source the change touches is validated, approved and activated together, and if one is refused the ones already switched are put back before you see the error. If you cannot publish, the same button becomes **Request publish**: the change stays saved with your name on it and shows at the top of Rules for anyone who can. ## Versions The live version is named at the top of Rules. **Versions** lists every publish with its author, summary and what changed, and offers **Roll back** on the live one and **Restore** on older ones. A restore is a publish of that older policy as a new version, through the same path, so nothing is edited in place and every step is on the ledger. ## Observing and enforcing The switch at the top of Rules says whether model-call refusals are applied or only recorded, and the number beside it says how many the week would have refused. Turning enforcing on is a platform-operator action; the confirmation names the number and says which planes follow: model calls enforce, prompt-content and tool-call rules keep observing until the gateway is armed for them. ## Read what happened **Activity** lists decisions as what happened: Blocked, Would have blocked, Asked a human, Allowed, or Could not decide, each naming the rule. **Why?** explains a refusal from the rule, the reason and the manifest. **Make a rule** opens the rule screen pre-filled from the decision. ## External hosts and managed shell commands Choose **Control external hosts** or **Control shell commands** from **New rule**. Select **Block** or **Ask a human**. For approval rules, choose either: - **Once:** approve this connection or exact command attempt. - **During this run:** approve repeated connections to the same host and port, or the same exact command and working directory, while this run remains active. Other command inputs remain bound too. This never grants unrestricted access for the run. Host rules accept exact hostnames, `*.example.com` for subdomains (not the base domain), and an optional port. Command rules name executables such as `rm` or `curl`. Dynamic shell expressions are treated conservatively as potentially matching command rules. The operation waits in the **Supervision** inbox until a human approves or refuses it. Unanswered asks expire after 30 minutes. A network client may time out earlier while waiting; approval does not restart a request that the client has already abandoned. Approval cannot override a hard block or the agent's baseline capability limits. Changing or restoring the action policy requires a fresh approval. **Observing** records the decision without stopping to ask a human. Activity distinguishes **Asked a human** from **Approved**; approval is not proof that execution succeeded. **Coverage:** host rules check new external connections at the host proxy. An HTTPS connection can carry multiple requests; this is not inspection of individual URLs or requests. Command rules check managed shell tool input before execution. They do not intercept arbitrary subprocesses or prevent file deletion through Python, scripts, or other programs. Filesystem policy enforcement is not part of this feature. Updated host workers and Claude or Deepagents runtimes are required before starting new runs, including while observing, so switching to enforcement cannot bypass checks. Enabling external host or command enforcement applies to runs that start after the switch. Runs that started earlier, including suspended runs that resume later, keep observing until they end: their matches are recorded as would-deny or would-ask and the Assurance Ledger entry says the run started before enforcement. The **Policy enforcement** dialog lists those runs with a **Stop** control for each. An unsupported runtime is refused rather than silently bypassing an enforcing rule. These rules use the `cedar / actionfacts/v1` family. Cedar evaluates the conditions; validated annotations declare the approval requirement and its scope: ```cedar @id("approve-partner-host") @effect("require-approval") @approval_scope("run") forbid(principal, action == Action::"network.connect", resource) when { context.target == "api.partner.example" && context.port == 443 }; ``` The platform manages the human decision and checks its run, operation, and active policy binding. A matching unannotated `forbid` remains a hard block. --- Source: /guides/manage/organization-members # Organization members and access Invite people, set roles, grant tenants, transfer ownership, and read the audit trail, from the Console or from tanomis. An organization's owners and admins manage who belongs to it and which tenants each person can reach. Everything here is available in the Console under the organization settings and from `tanomis org`. The server decides every call; the CLI reports refusals as given. - Roles: **owner**, **admin**, **developer**, **viewer**. - Owners and admins reach every tenant. Developers and viewers reach only tenants they are granted. - Accepting an invitation grants the organization's default tenant automatically. - Every change lands in the audit trail. ## Invite a member ```bash title="Terminal" tanomis org invite dana@example.com --role developer tanomis org invitations ls tanomis org invitations revoke INVITATION_ID ``` The invitee accepts from the Console link in the email, or with `tanomis org accept --token`. If the organization has a default tenant, the new member is granted it on acceptance; otherwise grant a tenant next. ## Grant and revoke tenant access Developers and viewers see only the tenants named in a grant: ```bash title="Terminal" tanomis org grants ls tanomis org grants add PRINCIPAL_ID --tenant northwind-sandbox tanomis org grants revoke GRANT_ID ``` Capabilities on a grant come from the vocabulary `manage`, `deploy`, `run`, `view`. A grant takes effect on the member's next request; so does a revocation. ## Change roles and ownership ```bash title="Terminal" tanomis org members tanomis org members set-role MEMBER_ID --role admin tanomis org members remove MEMBER_ID --yes tanomis org ownership transfer MEMBER_ID ``` Transferring ownership always steps the current owner down; add `--demote-self` to choose the role you keep. `members remove` asks for `--yes` because it is not reversible from the CLI. ## Read the audit trail ```bash title="Terminal" tanomis org audit ``` Owners and admins read it. See [Audit and evidence](/guides/audit/audit-and-evidence) for what the trail contains and proves. ## What members can see The reads behind these controls are owner-or-admin. A developer or viewer who opens the organization settings sees an explanation instead of the controls, and `tanomis org` returns the server's refusal. ## Organization and tenant lifecycle Creating an organization, attaching or detaching a tenant, setting the default tenant, and suspending or deleting an organization are platform operations performed with your Tanomis administrator, not self-service. Ask for them through your support channel. ## Where to go next - [Organizations and tenants](/concepts/organizations-and-tenants): the model behind roles, grants, and workspaces. - [Authenticate and select a workspace](/getting-started/authenticate-and-select-workspace): what a newly granted member does first. --- Source: /guides/manage/sso-and-scim # SSO and SCIM Sign members in through your identity provider and sync groups automatically. Members sign in with the hosted identity provider today. Enterprise single sign-on and group synchronisation are planned. This page is planned and not yet written, because the capability it describes is not yet generally available. The structure is here so you can see where it fits; the notice comes off when it ships. ## What this page will cover - Connect an enterprise identity provider. - Map identity provider groups to roles and tenant grants. - Deprovisioning. ## Meanwhile - [Organization members and access](/guides/manage/organization-members) covers invitations and grants today. --- Source: /guides/manage/tenants-and-environments # Customer tenants Create, inspect, retry, and offboard organization tenants from the Console or CLI. A tenant is where agents run and their data stays. Your organization can own several customer tenants and a sandbox tenant for testing. Each tenant belongs to one organization. ## Create and inspect tenants An organization owner or admin can open **Settings → Organization → Tenants** in the Console to create a tenant and inspect its progress. The equivalent CLI commands use a human login: ```bash tanomis org tenants create bluewater-bank tanomis org tenants ls tanomis org tenants get "$TENANT" ``` Use `--org ORGANIZATION` to choose an organization explicitly. Creation returns before provisioning finishes. A tenant is usable when its status is `active` and its organization attachment state is `ready`; inspect both with `tanomis org tenants get "$TENANT" -o json`. Provisioning can take several minutes. Deployment policy controls the tenant limit and supported provisioning modes. If provisioning fails, inspect the tenant and retry after the underlying issue is resolved: ```bash tanomis org tenants retry "$TENANT" ``` ## Grant access Owners and admins see attached tenants. Developers and viewers need a tenant grant; see [Organization members and access](/guides/manage/organization-members). Use `tanomis workspace ls` to see the workspaces available to your human login. Organization-owned [automation clients](/guides/deploy/automation-credentials) automatically reach all active, ready tenants in the organization, including a newly created tenant as soon as it is ready. They do not use per-tenant grants. A sandbox CI job's tenant selection is therefore not a restriction on the underlying credential. ## The default tenant The default tenant is granted to new members when they accept an invitation. It can also supply a default selection when a tenant is omitted. An organization with no default auto-grants nothing. Creating a tenant does not automatically make it the default. Changing the default or attaching an existing deployment tenant remains an operator operation. A self-hosted installation starts with a default organization. The operator configures the installation and its initial access; owners and admins then use the same organization management flow as hosted customers. ## Sandbox and customer tenants Grant developers access to the sandbox and only the access they need in customer tenants. Set tenant policy independently. With a tenant-owned automation deployment, deploy the reviewed source to each tenant's own agent registration; do not assume a version owned by one tenant belongs to the other. See [Deploy from CI](/guides/deploy/deploy-from-ci). ## Offboard a tenant Offboarding immediately withdraws machine access and starts asynchronous resource cleanup. It is irreversible. Review the tenant and preserve any data you need before confirming in the Console, or supply its exact ID to the CLI: ```bash tanomis org tenants offboard "$TENANT" --confirm "$TENANT" ``` Inspect the tenant until cleanup completes. The resources removed depend on the deployment's provisioning mode. An automation client cannot create, retry, or offboard tenants; these operations require a human owner or admin. ## Where to go next - [Organizations and tenants](/concepts/organizations-and-tenants) - [Tenant CLI reference](/cli/org/tenants) - [Organization management API](/reference/organization-management-api) --- Source: /guides/manage/workspaces # Workspaces What a workspace is, how to switch between them in the CLI and the Console, and why selecting one never widens what you can do. A workspace is your view of one organization and one of its tenants: the pair, your role there, and the capabilities you hold. It is a selection, not a resource. You choose it once per context; the server re-derives your access from the token, the organization, and the tenant on every request. - A workspace is one organization plus one tenant, with your role and capabilities there. - `tanomis workspace use ORG --tenant TENANT` selects it per context; `show` prints it. - Selecting supplies context and never widens access; grants are re-checked on every request. ## Switch in the CLI ```bash title="Terminal" tanomis workspace ls tanomis workspace use northwind --tenant northwind-sandbox tanomis workspace show tanomis workspace clear ``` `use` persists the selection on the active context; `show` prints it beside the role and capabilities; `clear` removes it. Tenant-scoped commands then send the organization and tenant selectors for you, and refuse an explicit `--tenant` that disagrees with the selection. ## Switch in the Console The organization and tenant you are working in appear in the header. Settings shows the workspaces you can select. Where the switcher is not yet available in your environment, sign in to the organization you need. ## Switch in your application Applications do not select a workspace; they send it on every request as the `X-Organization-ID` and `X-Workspace-Tenant-ID` headers, from the pair returned by [List workspaces](/api/workspaces/list-workspaces). ## What selecting cannot do Selecting a workspace supplies context. It cannot attach a tenant, grant one, or reach one you are not granted; a selection outside your grants fails at the first server-backed call. Grants can change at any time and take effect on the next request. ## Where to go next - [Organizations and tenants](/concepts/organizations-and-tenants) - [Authenticate and select a workspace](/getting-started/authenticate-and-select-workspace) --- Source: /guides/run/answer-approvals-in-the-console # Answer approvals in the Console Where a paused run shows up in the Console, how to answer a permission request or a question, and how the Supervision inbox orders what is waiting. When an agent reaches an action you marked as approval-required, or asks a question, the run pauses and the request appears in two places: inline in the session, and in the Supervision inbox across the tenant. Answering from either resumes the same run. - **Sessions** shows the request as a card in the run's timeline, with allow and deny, or an answer box for a question. - **Supervision** lists every pending request in the tenant in its Inbox, longest-waiting first, with a "Mine" filter for the sessions you started. - The first answer wins. A second reviewer sees the recorded decision, not a second prompt. - A request has an expiry; an expired request ends the run's wait with a refusal, recorded. - Only a person can answer. A request signed with an agent's delegated credential is refused. ## In the session Open Sessions, select the session, and find the card at the point where the run paused. A permission card names the tool and shows its input; a question card shows the question. Choose Allow or Deny, or type the answer, and the run continues from where it stopped. The card stays in the timeline as the record of the decision and who made it. ## In the Supervision inbox The Supervision inbox collects pending requests from every session in the selected tenant, ordered by how long each has waited, so a reviewer can work through them without opening sessions one by one. Each row names the agent, the tool or question, who started the session, how long it has waited, and when it expires. Approve, deny (with an optional reason), or answer directly on the row; "Open session" takes you to the full timeline. Two filters shape the list. **Mine** shows requests from sessions you started and is the default for developers and viewers. **All in tenant** shows every session's requests and is the default for organization owners and admins. Either way, any member of the tenant may answer any request; the filter is a view, not a permission. After you answer, the row reads "Answered, delivering" until the agent has received it, then "Delivered". A parked session is restored to deliver the answer; viewing the inbox never restores one. New requests appear within one reconciler sweep, so the header shows when the list was read rather than claiming to be live. Two identical requests from one session show as one row. ## Rules the Console follows - A decision is accepted exactly once. If someone else answered first, you see their answer. - Closing the tab does not lose the request. It is a control-plane record, and the agent's runtime may be suspended while it waits; it is restored when you answer. - Denying a permission does not fail the run. The agent learns the tool was refused and decides what to do next, which may be to finish without it or to ask. - Everything here is also available to your application through the interactions API, so a product can present the same decision in its own interface. See [Approval-required actions](/guides/run/approval-required-actions). ## Where to go next - [Tour the Console](/getting-started/console-tour) - [Policy, approvals, and evidence](/concepts/governance): where approvals sit among the other controls. --- Source: /guides/run/approval-required-actions # Approval-required actions Pause an agent at the actions you choose, show a person exactly one decision, answer once, and follow the same run to completion. An agent does not have to guess at a consequential action or missing context. It stops at a durable **interaction**, your application brings a person in, and the same run continues after the answer. This guide builds that application-side flow against the Platform API: permission decisions and questions, disconnects, competing reviewers, expiry, and a runtime that suspends while it waits. - Declare the moments in `agent.yaml`: `approval_required_tools` for permission decisions, `enable_ask_user` for questions. - When the run pauses, the session reports `awaiting_input: true`; list its pending interactions and show the person exactly one decision. - Answer by the stable `interaction_key`. A decision is accepted once; a second answer sees the recorded one. - Interactions are durable: they survive closed tabs, restarts, and a suspended runtime, and they expire on a deadline you can read. > **Availability:** Durable approvals and questions are on in hosted Tanomis and operator-enabled when self-hosted. The examples below are the supported wire contract. See [What is available where](/reference/availability). ## Declare the moments The deployed version opts into the interactions it can emit: ```yaml title="agent.yaml" policy: approval_required_tools: - Bash enable_ask_user: true ``` `approval_required_tools` makes each listed harness tool wait for a `permission` decision; `enable_ask_user` lets the harness ask a `question` and wait for a string answer. These are runtime interactions, separate from `approval_thresholds` in the held scoped-token contract. Use an explicit session and run so your application holds stable identifiers before an interaction appears; see [Sessions and runs](/concepts/sessions-and-runs). The API enforces tenant access but does not assign an interaction to one approver: enforce your own reviewer roles before exposing Approve or Deny. ## Two commits to watch The run remains `running` while it waits. `answered` means the control plane durably accepted one answer; `delivered` means it handed that answer to the agent service or its durable replay journal. Only the run's later terminal status tells you how the work ended. Content-identical waits can share this review unit; the diagram shows the ordinary single-run path. 1. **Person → control plane.** The first valid answer moves the row from `pending` to `answered` and returns `202 Accepted`. 2. **Control plane → agent.** Tanomis restores a suspended session if needed, delivers the answer idempotently by `interaction_key`, and moves the row to `delivered` after a successful handoff. A `202 Accepted` response moves the durable row to `answered`; it does not collapse these two commits or prove that the agent received the answer. Neither commit approves the final action: a delivered `approve` lets the agent continue toward a tool call, and the run's terminal status says whether the work succeeded. ## 1. Discover the pending interaction Keep the run's event stream open, and independently list durable pending rows. The row is the recovery source after a disconnect or reload: ```bash curl --silent --show-error --fail-with-body \ "$TANOMIS_API/v1/sessions/$SESSION_ID/interactions?status=pending" \ --header "Authorization: Bearer $TANOMIS_TOKEN" \ --header "X-Organization-ID: $TANOMIS_ORG" \ --header "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` ```json title="A permission row" { "interaction_key": "ihk_4f72", "run_id": "run_a12bc3", "kind": "permission", "status": "pending", "payload": { "tool_name": "Bash", "tool_input": { "command": "kubectl rollout restart deployment/api -n production" } }, "created_at": "2026-09-04T08:31:12Z", "expires_at": "2026-09-04T09:31:12Z" } ``` ```json title="A question payload" { "question": "Which region should receive the canary?", "header": "Choose a region", "options": [ { "label": "eu", "description": "EU production" }, { "label": "us", "description": "US production" } ], "multi_select": false } ``` `interaction_key` is an opaque, session-scoped review address. Store it with the session and run IDs exactly as returned and URL-encode it as one path segment; never derive it, use an event's request ID, or reuse it in another session. The key identifies interaction **content**, so content-identical asks in one session can share a row and one answer settles every matching ask parked at that moment. A row therefore proves one **review unit**, not one tool execution. If policy needs a separate decision for every repeated identical action, give each its own session and run. ## 2. Validate before showing a decision | Check | Safe behaviour | |---|---| | Session and tenant | The session URL and selected tenant are the ones you intended. | | `run_id` | Matches the run whose context you are showing. | | `interaction_key` | The exact opaque key from the row. | | `kind` | `permission` or `question`; reject anything else. | | `status` | Still `pending`; a terminal row is not actionable. | | `expires_at` | Still in the future; use the row's value, not an assumed TTL. | | `payload` | Permission: show the tool and input. Question: show the prompt and options. | An oversized payload is abridged to `tool_name` plus `"payload_omitted": true`. Do not turn missing context into a blanket approval; recover context through your application or deny. Escape payload text when rendering it: it is agent-produced review material, not trusted HTML. ## 3. Submit exactly one kind-specific answer ```text POST /v1/sessions/{session_id}/interactions/{interaction_key}/answer ``` ```json title="Permission" { "kind": "permission", "answer": { "decision": "approve", "reason": "Reviewed change request CR-1842." } } ``` ```json title="Question" { "kind": "question", "answer": "eu" } ``` To refuse a permission, send `"decision": "deny"` with a useful reason. For a question, `answer` is the string itself—not an object containing a second `answer` field. A multi-select answer is the chosen labels as one comma-separated string. Send `kind` so a mismatch fails before the answer is stored, and keep the encoded answer at or below 65,536 bytes. The API stores `answer` as opaque JSON and validates the row's `kind`, not the inner shape, so validate that shape in your backend. The first `pending → answered` transition wins; disable the controls after submitting, but expect another tab or reviewer to win the race. ## 4. Reconcile acceptance and ambiguity On `202 Accepted`, show **Answer accepted**, not **Action approved**. Then list the session's interactions and follow the same key until it settles. Use the same read-after-write when the POST times out; never send a different answer because the client missed the response. ```bash curl --silent --show-error --fail-with-body \ "$TANOMIS_API/v1/sessions/$SESSION_ID/interactions" \ --header "Authorization: Bearer $TANOMIS_TOKEN" \ --header "X-Organization-ID: $TANOMIS_ORG" \ --header "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" ``` | POST or row result | Meaning | Next action | |---|---|---| | `202`, row `answered` | One answer is durably accepted; delivery is pending. | Poll with backoff and keep observing the same run. | | row `delivered` | Tanomis handed the answer to the agent runtime or its replay journal. | Continue to the run's terminal status. | | `409 interaction_already_answered` | Another caller won the first-answer race. | Read the row and use the recorded answer. | | `409 interaction_terminal` | The row is `expired` or `cancelled`. | Retire the UI action and read the session and run. | | `404` | The key or session is absent in the selected tenant; cross-tenant existence is hidden. | Recheck tenant context and stored identifiers. | | `400` | The filter or top-level kind is invalid, or the answer is absent, malformed, or oversized. | Correct the request. | The row records the answer and timestamps, but it does not expose an approver identity. If your audit needs the person and business reason, authenticate them in your application and keep that record beside the session, run, and interaction keys. ## 5. Let Tanomis attempt to restore the waiting runtime Do not call `POST /resume` for an interaction wait. If Tanomis suspended the runtime while the person was thinking, the delivery worker makes a bounded number of restore attempts; after that, the row stays retryable and delivery waits until another supported request makes the session live. `answered` can therefore persist; only `delivered` proves the handoff. Keep the original event stream, reconnecting with its stored `Last-Event-ID` and bounded backoff, and read the original run until it is `succeeded`, `failed`, or `cancelled`. A pending interaction adds no run status: the run stays `running`, and a session read may show `awaiting_input: true`. Do not start a replacement run. If a row stays `answered` beyond your threshold, keep the session, run, and interaction keys and contact your platform operator. Your completion record should pair the interaction key, kind, accepted answer, and `answered_at`/`delivered_at`; the session and run IDs; the run's terminal status; and the terminal event or application outcome you rely on. ## 6. Close expired and cancelled requests safely After `expires_at` passes, retire the controls and refresh the row. Expiry and answer race safely: whichever claims the pending row first stands. An `expired` row is no longer answerable; Tanomis winds the waiting agent down with a synthetic denial or an empty answer, and the row stays `expired`. Never present that synthetic value as a human decision. A row becomes `cancelled` when its run or session ends before it is consumed, including an accepted row whose session became terminal while delivery was queued: the human answer was stored but the agent did not receive it. For either terminal state, disable the controls, preserve the record, and read the session and run before offering a new operation. A new operation is a new run; a fresh decision on content-identical work belongs in a new session. ## Application checklist - Create an explicit session and run; store both IDs before processing events. - List `pending` rows independently of the event stream; deduplicate by session plus `interaction_key`. - Validate tenant, run, kind, status, expiry, and payload before rendering the decision. - Enforce your approver roles and escape agent-produced content. - Send the exact kind-specific answer once. - Treat `202` as acceptance; reconcile the row through `delivered`, `expired`, or `cancelled`. - On timeout or `409`, read the winning row; never replace its answer. - Let Tanomis restore a suspended wait; require `delivered`, then follow the same run to an authoritative terminal state. ## Where to go next - [Human interactions in the Platform API](/api/interactions/list-interactions): the exact fields. - [Answer approvals in the Console](/guides/run/answer-approvals-in-the-console): the same decision without writing code. - [Sessions and runs](/concepts/sessions-and-runs#handle-a-run-waiting-for-a-person): the surrounding lifecycle. --- Source: /guides/run/attach-files # Attach files to a session Give an agent documents to work on: prepare the file, upload its bytes, and reference it when you start a run. A session can hold files that runs in that session read. Files are first-class resources with their own ids, integrity checks, and lifecycle, so an upload is never a blind write into an agent's workspace. - Check `session_files_supported` on the session first. - Prepare a file with its name, size, and `sha256`, upload the bytes, then reference its `file_id` in a run's `attachments`. - Files belong to the session; list, download, and delete them there. > **Availability:** Read `file_attachments_enabled` from [Inspect the caller](/api/workspaces/whoami), then `session_files_supported` on the session; the deployed version must also report file support. See [What is available where](/reference/availability). ## 1. Check support on the session Create the session as usual, then read it. Continue only if `session_files_supported` is `true`. ## 2. Prepare the file Declare the file before sending bytes, with its size and a SHA-256 digest of the content: ```json title="POST /v1/sessions/{session_id}/files" { "filename": "case-summary.pdf", "media_type": "application/pdf", "size_bytes": 48213, "sha256": "83e0ef2f426dfad68f9b420304fc19414f8451a6bf24667f7e61c50e9ca57f14", "idempotency_key": "case-summary-v1" } ``` The response carries the file's `id`, its `status` (`prepared`), and a `content_url`. ## 3. Upload the bytes Send exactly `size_bytes` bytes to the content URL as `application/octet-stream`: ```bash title="Terminal" curl -X PUT "$TANOMIS_API/v1/sessions/$SESSION_ID/files/$FILE_ID/content" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: $TANOMIS_ORG" \ -H "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" \ -H "Content-Type: application/octet-stream" \ --data-binary @case-summary.pdf ``` The file becomes `ready` when the digest matches what you prepared. A size or digest mismatch is refused rather than stored. ## 4. Reference it in a run ```json title="POST /v1/sessions/{session_id}/runs" { "input": { "prompt": "Summarize the attached case." }, "attachments": [{ "file_id": "file_b172c9" }], "idempotency_key": "case-summary-run-v1" } ``` The agent sees the file in its session workspace. Files belong to the session: a run in another session cannot reference them. `GET /v1/sessions/{session_id}/files` lists the file with `"status": "ready"` before you start the run. A `prepared` file that never became `ready` was not uploaded completely. ## Manage files - **List**: `GET /v1/sessions/{session_id}/files` - **Download**: `GET /v1/sessions/{session_id}/files/{file_id}/content` - **Delete**: `DELETE /v1/sessions/{session_id}/files/{file_id}` ## Where to go next - The endpoints with samples: [Prepare a file](/api/files/prepare-file) and the rest of the Files group in the [Platform API](/api). - [Invoke from your application](/guides/run/invoke-from-your-application) for the surrounding session and run flow. --- Source: /guides/run/authenticate-your-backend # Authenticate a backend Discover OAuth settings and use an organization machine client to authenticate platform API requests. Create an [automation client](/guides/deploy/automation-credentials) with the `invoke` profile for an application that runs agents, or `observe` for a read-only integration. Keep its secret on your backend. Never embed it in browser code or a mobile application. Your service needs three configuration values: the platform API origin, client ID, and client secret. For hosted Tanomis, the origin is `https://api.tanomis.com`. If you use `tanomis` in your service, the [three environment variables](/guides/deploy/automation-credentials#give-tanomis-three-values) handle the following steps automatically. - Three values: the platform API origin, the client ID, and the client secret. Never in a browser or a mobile app. - Discover the token endpoint from the platform; exchange the credentials with OAuth client credentials; send the token as a bearer with a tenant selector. - Effective permissions are the intersection of the token's scopes and the client's current permissions; a denial is an access decision, not a reason to fetch more tokens. ## Discover authentication settings Request the public configuration from your trusted platform origin: ```http GET /v1/cli-config HTTP/1.1 Host: api.tanomis.com ``` The response's `m2m` object supplies `issuer`, `audience`, and `scopes`. If it is missing, machine authentication is not available on that deployment. Fetch the issuer's OpenID Connect discovery document at `issuer + /.well-known/openid-configuration` and read `token_endpoint`. For deployments whose issuer is a base origin, `tanomis` also supports discovery at `issuer + /oidc/.well-known/openid-configuration` if the first location is unavailable. Do not substitute the audience for the platform origin. An audience is the identifier of the resource receiving the token and need not be a reachable address. Discover it rather than hardcoding a provider's domain or a hosted deployment's identifier. ## Exchange the credentials Use your backend's HTTP or OAuth library to POST to the discovered token endpoint with HTTP Basic authentication using the client ID and secret. Send form-encoded fields: ```text Content-Type: application/x-www-form-urlencoded Authorization: Basic grant_type=client_credentials resource= scope= ``` The body above lists the fields separately for readability; your library must URL-encode them as one form. Requesting a scope does not grant it: effective permissions are the intersection of the token's scopes and the client's current permissions. Use HTTPS for discovery and token requests, validate certificates, and do not forward credentials through redirects. Read the returned `access_token`, `token_type`, and expiry in memory. Reuse an unexpired token and obtain a new one before expiry; this flow does not use a human refresh token. Keep token responses and authorization headers out of application logs. ## Call the platform Send the token to the platform API origin and select the target tenant for tenant-scoped requests: ```http GET /v1/agents HTTP/1.1 Host: api.tanomis.com Authorization: Bearer X-Tenant-ID: ``` The tenant must be active and ready in the client's organization. Use the public platform API for agent, session, and run requests. Do not send organization machine tokens directly to an independent Registry endpoint. Disabling or deleting the client, removing a scope, or offboarding the tenant can deny a request while the token is still unexpired. Treat a permission denial as an access decision, not a reason to endlessly fetch new tokens. Machines cannot call the human-only credential or tenant lifecycle APIs to restore their own access. ## Where to go next - [Invoke from your application](/guides/run/invoke-from-your-application): the six requests that run an agent. - [Automation credentials](/guides/deploy/automation-credentials): profiles, rotation, revocation. - [Platform API](/api): every endpoint. --- Source: /guides/run/delegate-to-another-agent # Delegate work to another agent Let one agent hand a unit of work to another with narrowed authority, wait for the result, and keep every decision in the audit trail. A supervisor agent can hand a task to a specialist agent: "summarize this thread", "reconcile this invoice". Tanomis treats the hand-off as a governed call between two versions in the same tenant, with the callee's authority narrowed to what the caller was allowed to give it. > **Availability:** Runtime delegation is enabled per platform deployment; where it is off, the delegation tools are absent from the agent's bind. See [What is available where](/reference/availability). - The **caller** lists the agents it may hand work to under `delegation.calls`. - The **callee** lists who may call it under `delegation.accepts`, and optionally the `skills` it offers. Without skills it exposes one default skill that takes a prompt. - The two manifests are the grant. Nothing is created out of band, and nothing has to be redone when either side deploys a new version. - Every attempt, admitted or refused, is a ledger event carrying the full authority chain. ## Declare the two halves The callee advertises what it accepts: ```yaml title="agent.yaml (callee)" delegation: accepts: - tenant skills: - name: summarize description: Summarize a support thread and return the key facts and the customer's ask. policy: scope_ceiling: - "llm:your-model-family:invoke" ``` The caller names the agents it may call: ```yaml title="agent.yaml (caller)" delegation: calls: - summarizer policy: scope_ceiling: - "llm:your-model-family:invoke" ``` Naming a target puts the delegation tools on the caller's bind and is the caller-side ceiling: an agent cannot reach a callee it did not name. Whether any particular call is permitted is still decided per call. Renaming a skill is a breaking change for every caller that lists it. ## How a call is decided On each delegation the control plane, not the agents, decides: 1. The caller and callee versions are resolved from platform-attested identities, not from anything the agent claims. The callee is the version currently enabled for the tenant. 2. The caller's manifest must name the callee (and, if narrowed, the skill) under `calls`, and every enabled callee version must name the caller under `accepts` and offer the skill with one contract. 3. The callee's effective authority is the intersection of what the caller holds and the callee's own `scope_ceiling`. A scope the caller does not hold is a hard refusal, never a silent trim. An agent with no ceiling cannot receive a delegated grant at all. 4. Tenant policy is evaluated with the tenant's posture. 5. The decision is recorded, and only then is the callee's run dispatched. Authority narrows at every hop and never widens. A callee that delegates further can only pass on what it was granted. ## What the caller does at run time The caller sees a small set of tools: | Tool | Purpose | |---|---| | `start_delegation` | Hand a skill invocation to a callee and receive a delegation id | | `await_delegation` | Wait for the result; the caller can be suspended while it waits, so waiting costs nothing | | `check_delegation` | Read the state without waiting | | `list_delegations` | See the delegations this run started | | `update_delegation`, `cancel_delegation` | Adjust or withdraw work that has not completed | The callee runs as its own session and run in the same tenant, with the same event stream, approvals, and evidence as any other run. The caller's run resumes when the result arrives. ## Evidence Each attempt produces one `agent.delegate` audit event with the caller, the callee, the skill, the narrowed scopes, the authority chain, and the decision, whether it was admitted or refused. Read it with `tanomis org audit`; see [Audit and evidence](/guides/audit/audit-and-evidence). ## Where to go next - [Identity and access](/concepts/identity-and-access): the two identities on every request and why delegated authority narrows. - [Agent manifest](/reference/agent-manifest#agent-to-agent-delegation): every field in the delegation block. --- Source: /guides/run/durable-work-and-settlement # Durable work and settlement Bind a run to a unit of durable work that outlives the session, continue it later, and understand what settlement mode adds. Most agents answer a request and are done. Some serve a piece of work that has its own identity: an invoice to reconcile, a case to close. Durable work gives that unit a record the platform manages, so a later session can continue it and its outcome is settled once. > **Availability:** Durable work and settlement are enabled per tenant. A `settlement`-mode version fails session activation where the boundary is unavailable rather than running without it. See [What is available where](/reference/availability). - `settlement_mode` in the manifest is `standard`, `work`, or `settlement`, in that order of strength. Missing means `standard`. - In `work` and `settlement` modes, the first run in a new session declares the work it serves: `work.subject` to open new work, or `work.work_id` to continue existing work. - The admitted run returns `settlement_work_id`. Store it if another session may continue the work. - Work ends in one of two ways decided by the platform, never by a person editing its state. ## Declare the mode ```yaml title="agent.yaml" settlement_mode: work ``` | Mode | Behaviour | |---|---| | `standard` | The existing behaviour: runs are units of execution and nothing more. | | `work` | The agent produces durable work records. Runs bind to work. | | `settlement` | As `work`, and the session must establish the managed worker and proxy boundary or fail activation, so every effect the work dispatches is enforced through policy. | ## Open or continue work ```json title="POST /v1/sessions/{session_id}/runs" { "input": { "prompt": "Reconcile invoice INV-1842." }, "work": { "subject": "invoice:INV-1842", "contract_ref": "billing/reconciliation", "contract_version": "2026-09" }, "idempotency_key": "invoice-1842-attempt-1" } ``` Send either `subject` or `work_id`, never both. When opening work, `contract_ref` and `contract_version` may name a contract and must be sent together; later requests may omit the pair or restate it exactly, but cannot add or change it. The response's `settlement_work_id` is the handle another session uses with `work.work_id`. A work item has at most one live run at a time; a second run against the same work while one is live returns `409` with `work_has_live_run`. ## What settlement adds In `settlement` mode the effects a run dispatches to other systems, not the messages it exchanges, are the things being settled. Each is authorized through the policy facade before it happens and recorded as it happens, and the work's outcome is decided from those records. Work ends two ways: it settles, or it is abandoned by the platform's rules. A person does not end it by hand, which is what makes the record trustworthy. ## Where to go next - [Sessions and runs](/concepts/sessions-and-runs): where work sits beside sessions and runs. - [Start a run](/api/runs/start-run): the request fields. - [Agent manifest](/reference/agent-manifest#state-warm-starts-and-sessions): the mode field. --- Source: /guides/run/event-triggers # Event triggers Start a run when something happens elsewhere: a webhook, a queue message, or a platform event, with the payload delivered to the agent. Event triggers start a run in response to an external or platform event instead of a direct call. The event's payload reaches the agent as the run's input, and the trigger is governed and audited like any other way of starting a run. This page is planned and not yet written, because the capability it describes is not yet generally available. The structure is here so you can see where it fits; the notice comes off when it ships. ## What this page will cover - Trigger sources: webhooks, queues, and platform events such as a run completing. - Verifying and shaping the incoming payload before it reaches the agent. - Mapping an event to a session: new session per event or continue an existing one. - Retries, deduplication, and how triggered runs appear in evidence. ## Meanwhile - [Invoke from your application](/guides/run/invoke-from-your-application) shows how to start a run from your own webhook receiver today. - [Stream and replay events](/guides/run/stream-and-replay-events) covers the events a run emits, which a trigger could react to. - [Delegate work to another agent](/guides/run/delegate-to-another-agent) is the governed way one agent starts work in another today. --- Source: /guides/run/fork-a-session # Fork a session Start a new session from a point in an existing session's history, with its own lineage and evidence. The Console can fork a session from a point in its timeline. The API surface for forks is not yet part of the public Platform API. This page is planned and not yet written, because the capability it describes is not yet generally available. The structure is here so you can see where it fits; the notice comes off when it ships. ## What this page will cover - Fork from the Console and from the API. - What the fork inherits: workspace files, history, and identity. - How lineage appears in evidence. ## Meanwhile - [Sessions and runs](/concepts/sessions-and-runs) explains why a later run is not a fork. - [Tour the Console](/getting-started/console-tour) shows where forking lives. --- Source: /guides/run/invoke-from-your-application # Invoke from your application Call a deployed agent from your own backend: discover the workspace, create a session, start a run, and follow it to a result. Once a version is enabled for a tenant, your product calls it over HTTP. The sequence is the same for every agent: discover your workspace, create a session, start a run, follow its events, read the result. You need an access token for the Tanomis API audience and an agent enabled for the tenant you will select. A backend gets its token from an automation client with the `invoke` profile; see [Authenticate a backend](/guides/run/authenticate-your-backend). The CLI's human login is not an application credential. ## 1. Discover the workspace once Ask which organizations and tenants the token may use, and keep the pair you choose: ```bash title="Terminal" curl "https://api.tanomis.com/v1/me/workspaces" \ -H "Authorization: Bearer $TANOMIS_TOKEN" ``` Copy `organization_id` and the selected tenant's `tenant_id` into the two workspace headers on every later request: ```text X-Organization-ID: org_abc123 X-Workspace-Tenant-ID: customer-production ``` The headers select context. They cannot widen what the token grants. ## 2. Create a session A session is the continuity boundary for one enabled agent: it keeps the agent's workspace, serializes runs, and is what you retry against. ```bash title="Terminal" curl -X POST "https://api.tanomis.com/v1/sessions" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: org_abc123" \ -H "X-Workspace-Tenant-ID: customer-production" \ -H "Content-Type: application/json" \ -d '{"agent_name":"support-copilot","idempotency_key":"support-case-1842"}' ``` Send an `idempotency_key` tied to your own unit of work. A retry with the same key and body returns the same session instead of creating another. ## 3. Start a run ```bash title="Terminal" curl -X POST "https://api.tanomis.com/v1/sessions/$SESSION_ID/runs" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: org_abc123" \ -H "X-Workspace-Tenant-ID: customer-production" \ -H "Content-Type: application/json" \ -d '{"input":{"prompt":"Draft a reply to this customer."},"idempotency_key":"support-case-1842-reply-1"}' ``` The response is the admitted run. Its `id` and `status` are the authoritative state from here on. ## 4. Follow the run Stream the run's events as server-sent events and store the last event id you processed: ```bash title="Terminal" curl --no-buffer "https://api.tanomis.com/v1/sessions/$SESSION_ID/runs/$RUN_ID/events" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: org_abc123" \ -H "X-Workspace-Tenant-ID: customer-production" \ -H "Accept: text/event-stream" ``` If the connection drops, reconnect with `Last-Event-ID` and the stream resumes after it. The terminal events are `run_completed`, `run_cancelled`, and `error`. When a stream ends without one, read the run and reconnect only while it is still non-terminal. The complete consumer loop, including deduplication, is in [Stream and replay events](/guides/run/stream-and-replay-events). ## 5. Handle a pause for a person If the agent reaches an action that needs approval, or asks a question, the run pauses and the session reports `awaiting_input: true`. List the session's pending interactions, show the decision to a person, and answer by the interaction's key. The run continues from where it paused. See [Approval-required actions](/guides/run/approval-required-actions). ## 6. Finish When no more work will use the session, stop it to release its runtime: ```bash title="Terminal" curl -X POST "https://api.tanomis.com/v1/sessions/$SESSION_ID/stop" \ -H "Authorization: Bearer $TANOMIS_TOKEN" \ -H "X-Organization-ID: org_abc123" \ -H "X-Workspace-Tenant-ID: customer-production" ``` A stopped session can be resumed later. Idle sessions are suspended automatically and restored when work arrives, so you do not have to stop a session between turns of the same conversation. ## Choosing between one-shot and sessions | You want | Use | |---|---| | One independent request, streamed, no retries to reason about | [Invoke once and stream](/api/agents/invoke-agent) | | Retries, several runs over the same context, file attachments, approvals, reconnection | An explicit session, as above | ## Where to go next - [Platform API](/api): every endpoint with request and response samples in cURL, Python, and TypeScript. - [Sessions and runs](/concepts/sessions-and-runs): state, retries, concurrency, recovery. - [Attach files to a session](/guides/run/attach-files): give the agent documents to work on. --- Source: /guides/run/scheduled-execution # Scheduled execution Run an agent on a schedule without a caller: define a cadence, choose what each run starts with, and follow the runs it produces. Today every run starts because a caller asks for it: your backend, the Console, or the CLI. Scheduled execution lets the platform start runs on a cadence you declare, with the same session, policy, and evidence rules as a run you start by hand. This page is planned and not yet written, because the capability it describes is not yet generally available. The structure is here so you can see where it fits; the notice comes off when it ships. ## What this page will cover - Declare a schedule for a deployed version and the tenants it runs for. - What each scheduled run starts with: prompt, workspace, and durable work. - Overlap, missed windows, and pausing a schedule. - How scheduled runs appear in events and evidence. ## Meanwhile - [Invoke from your application](/guides/run/invoke-from-your-application) shows how to start runs from your own scheduler today. - [Automation credentials](/guides/deploy/automation-credentials) covers the machine identity a scheduler would use. - [Durable work and settlement](/guides/run/durable-work-and-settlement) explains how a later run continues the same unit of work. --- Source: /guides/run/stream-and-replay-events # Stream and replay events Consume Tanomis run events, persist a cursor, reconnect safely, and confirm durable run status. Run events let your application render progress while an agent works. Treat the stream as a live delivery channel—not as the authoritative run record or a permanent audit log. A reliable consumer processes each event idempotently, stores a run-scoped cursor, and reads the run after every terminal signal or end-of-stream. This guide assumes you already have a session ID, run ID, and a supported access-token path. See [Sessions and runs](/concepts/sessions-and-runs) first if those resource boundaries are unfamiliar. - Open the stream with `Accept: text/event-stream`; every frame has an id, an event name, and the full event as JSON. - Store the last id you processed **after** processing it, and deduplicate by run id plus event id. - On disconnect, reconnect with `Last-Event-ID`; replay starts strictly after it. - Terminal events are `run_completed`, `run_cancelled`, and `error`. A stream can end without one, so the run resource decides. Event `41` is processed and stored → event `42` is processed → the connection breaks before cursor `42` is stored → reconnect sends `Last-Event-ID: 41` → event `42` arrives again and is ignored by deduplication → event `43` signals `run_completed` → `GET` run confirms authoritative status `succeeded`. If cursor `42` had been stored successfully, reconnecting with `Last-Event-ID: 42` would begin strictly after it. The duplicate above exists because application work completed before its cursor checkpoint did—not because the server replays the requested cursor. ## Open one run stream Read events for one session and run: `GET /v1/sessions/{session_id}/runs/{run_id}/events` ```bash curl --no-buffer --fail-with-body \ "$TANOMIS_API/v1/sessions/$SESSION_ID/runs/$RUN_ID/events" \ --header "Authorization: Bearer $TANOMIS_TOKEN" \ --header "X-Organization-ID: $TANOMIS_ORG" \ --header "X-Workspace-Tenant-ID: $TANOMIS_WORKSPACE" \ --header "Accept: text/event-stream" ``` The stream is scoped to this session and run. It does not provide ordering across runs or combine events from other sessions. ## Read complete SSE frames Wait for the blank line that ends a frame before processing it. Every public event frame contains: | SSE field | Contract | |---|---| | `id` | An opaque event ID. It orders events only inside this run. | | `event` | The event name. Only the three terminal names below have stable terminal meaning. | | `data` | One complete JSON event object, including its own `id`, `type`, `session_id`, `run_id`, and harness-specific `data`. | A synthetic stream can look like this: ```text id: 41 event: message_delta data: {"id":"41","type":"message_delta","session_id":"ses_example","run_id":"run_example","created_at":"2026-09-03T09:00:00.041Z","data":{"text":"Draft"}} id: 42 event: message_delta data: {"id":"42","type":"message_delta","session_id":"ses_example","run_id":"run_example","created_at":"2026-09-03T09:00:00.042Z","data":{"text":" ready"}} id: 43 event: run_completed data: {"id":"43","type":"run_completed","session_id":"ses_example","run_id":"run_example","created_at":"2026-09-03T09:00:00.043Z","data":{}} ``` `message_delta` is illustrative, not a universal schema promise. Event names and fields inside the harness-specific `data` object can evolve. ## Reconnect without double-applying Use this sequence for every connection: 1. Open the events endpoint with `Accept: text/event-stream`. If a cursor exists for this run, send it in `Last-Event-ID`. 2. Parse one complete frame. Verify its session and run IDs match the resource you requested. 3. Build a deduplication key from the run ID and event ID. If that key is already processed, skip the side effect and advance the stored cursor if needed. 4. Apply the event idempotently. Only after processing succeeds, record the deduplication key and persist the event ID as this run's cursor. 5. On `run_completed`, `run_cancelled`, or `error`, stop interpreting the stream and read the run resource for its authoritative lifecycle status and error details. 6. On a clean EOF without a terminal event, read the run. Reconnect with bounded backoff only while the run remains `queued` or `running`. Store the cursor with both the session ID and run ID. Treat the event ID as an opaque string; never compare IDs from separate runs. Where an event changes durable application state, make the state change and cursor checkpoint atomic when possible. Otherwise, make the handler idempotent so a replayed frame is safe. Reconnect by sending the last **successfully processed** event ID: ```http Accept: text/event-stream Last-Event-ID: 41 ``` When live or persisted history is available, replay starts strictly after that ID. ## Backend consumer pseudocode Keep bearer credentials and cursor storage in your trusted backend. This pseudocode names the required behavior without prescribing an SDK or storage engine: ```text cursor = load_cursor(session_id, run_id) loop: stream = open_events(session_id, run_id, last_event_id=cursor) for frame in stream: event = parse_complete_json_frame(frame) assert event.session_id == session_id and event.run_id == run_id key = (run_id, event.id) if already_processed(key): cursor = checkpoint(event.id) continue apply_idempotently(event) record_processed_and_checkpoint(key, event.id) cursor = event.id if event.type in {run_completed, run_cancelled, error}: break run = get_run(session_id, run_id) if run.status in {succeeded, failed, cancelled}: return run wait_with_bounded_backoff() ``` Do not start a new run when this stream disconnects. Reconnect to the same run with its cursor. ## Connect from a browser deliberately The browser's native `EventSource` constructor cannot attach an arbitrary `Authorization` header. Do not put bearer credentials in a query string to work around that boundary. Choose an architecture that matches your application's security model: - Stream through your authenticated same-origin backend, which keeps the Tanomis bearer token server-side and forwards only the events the signed-in user may see. - Use a fetch-based SSE parser only when the API is same-origin or your environment administrator explicitly configures CORS for your frontend origin and the required request headers. Your application must also be designed to hold and send the token in that browser context. Do not assume the hosted API accepts cross-origin browser requests. The same frame, cursor, deduplication, and final-read rules still apply. Browser tab closure, page navigation, and sleep are ordinary disconnects. Persist the run-scoped cursor somewhere that survives the recovery behavior your product promises. ## Handle closure, replay, and change | What you observe | What it means | What to do | |---|---|---| | A previously processed event arrives again | The stored cursor lagged successful processing, or the consumer is recovering around a failure boundary. | Deduplicate by run ID plus event ID; do not repeat the side effect. | | `200 OK` closes with zero frames | History may be absent, exhausted, or temporarily unreadable. This is a normal, fail-soft EOF—not proof of success. | Read the run resource. Reconnect boundedly only if its status is non-terminal. | | The stream closes after non-terminal events | Delivery ended before a terminal signal reached this connection. | Preserve the last processed cursor, read the run, and reconnect to the same run if needed. | | `run_completed`, `run_cancelled`, or `error` arrives | The stream emitted a terminal signal. | Stop consuming and read the run for authoritative lifecycle status and error details. | | An unknown event name or JSON field appears | A compatible harness or platform addition is present. Unknown events are not terminal. | Ignore it or render it generically; preserve fields you proxy and keep consuming. | | A frame cannot be parsed or fails scope checks | The frame was not processed successfully. | Do not advance the cursor; close the connection, inspect the run, and reconnect boundedly. | Persisted replay is best-effort and fail-soft. It does not guarantee that every prior event remains available, so do not use the event stream as your audit record. The final run resource tells you the durable lifecycle status even when the detailed transcript is unavailable. It does not reconstruct agent output or event payloads that your consumer missed. ## Protect event content Treat `data` as potentially sensitive customer and agent content. Depending on the harness and run, it can carry user input, model output, or tool-related details. Apply your product's authorization, redaction, retention, and display rules before storing or logging it. Avoid raw-frame logging by default; identifiers and event names are usually enough for transport diagnostics. ## Where to go next - [Stream run events](/api/runs/stream-run-events): exact request and response shapes. - [Sessions and runs](/concepts/sessions-and-runs): lifecycle, retries, cancellation. - [Approval-required actions](/guides/run/approval-required-actions): when the stream pauses for a person. --- Source: /guides/run/webhooks # Webhooks Receive an HTTP request from Tanomis when a lifecycle event happens, such as a run finishing or an approval being requested, instead of polling. Today your application learns that a run finished by following its event stream or polling its status. Webhooks let Tanomis call an endpoint you register when a lifecycle event happens, such as a run completing, failing, or pausing for an approval, so your backend can react without holding a connection open. This page is planned and not yet written, because the capability it describes is not yet generally available. The structure is here so you can see where it fits; the notice comes off when it ships. ## What this page will cover - Register an endpoint for a tenant and choose which lifecycle events it receives. - The delivery payload and how to verify its signature. - Retries, ordering, and deduplicating a delivery you have already handled. - How deliveries appear in evidence. ## Meanwhile - [Stream and replay events](/guides/run/stream-and-replay-events) is how your application follows a run to completion today. - [Invoke from your application](/guides/run/invoke-from-your-application) covers the run lifecycle a webhook would report on. - [Event triggers](/guides/run/event-triggers) is the inverse: an event elsewhere starting a run. --- Source: /reference/agent-manifest # Agent manifest Current agent.yaml settings, with types, defaults, validation rules, and effects. `agent.yaml` is the deployable contract for one agent version. This page describes the current authoring fields: what each setting accepts, what it defaults to, and what rejects a manifest at registration. For a guided introduction to the fields you touch first, read [Manifest essentials](/guides/build/manifest-essentials). Two rules apply everywhere: - **Unknown top-level keys are rejected.** A typo fails the manifest rather than being ignored. The one exception is a key prefixed `x-`, which is accepted and carried through untouched. - **A manifest is validated against a version, not an agent.** Settings are captured at registration and are immutable for that version; you change them by publishing a new version. ## A complete manifest Most agents need about this much. Every field links to the section that defines it. ```yaml title="agent.yaml" name: support-copilot # stable agent identity version: 0.2.0 # labels one immutable build framework: claude-agents-sdk # harness adapter entrypoint: agent:create_agent # python_module:function description: Triage and draft responses to support requests kind: interactive # interactive | background identity_mode: service # pinned by the first version model: claude-sonnet permitted_models: [claude-sonnet, claude-haiku] persistent: true # required for hosted versions session: idle_timeout: 30m max_lifetime: 24h resources: cpu: 100m memory: 512Mi network: egress: - api.zendesk.com # bare hostname, within the tenant ceiling policy: approval_required_tools: [Bash] # a person decides before these run enable_ask_user: true scope_ceiling: [llm:claude-sonnet:invoke] allowed_audiences: [https://api.tanomis.com] integrations: support: connector: global/support-desk # use an existing configured connector required: true tools: allow: [search_tickets, draft_reply] approval_required: [draft_reply] ``` ## Field index Grouped by the section that defines each field. "Hosted: yes" means the hosted control plane requires it; lenient non-hosted profiles may default it. ### Identity and packaging | Field | Type | Required | Default | |---|---|---|---| | [`name`](#identity-and-packaging) | string | yes | — | | [`version`](#identity-and-packaging) | string | in practice | — | | [`framework`](#identity-and-packaging) | enum | yes | — | | [`entrypoint`](#identity-and-packaging) | string | yes | — | | [`description`](#identity-and-packaging) | string | no | empty | | [`kind`](#behavioral-classification) | enum | yes | — | ### Models and tools | Field | Type | Required | Default | |---|---|---|---| | [`model`](#models) | string | no | resolved by the platform | | [`permitted_models`](#models) | list of string | no | empty | | [`permitted_tools`](#models) | list of string | no | empty | ### Attribution and identity | Field | Type | Required | Default | |---|---|---|---| | [`identity_mode`](#attribution-and-identity) | enum | hosted: yes | `service` only in lenient non-hosted profiles | | [`inherits_user_permissions`](#attribution-and-identity) | boolean | no | `false` | | [`identity.client_id`](#attribution-and-identity) | string | no | assigned at registration | ### State, sessions, and resources | Field | Type | Required | Default | |---|---|---|---| | [`persistent`](#state-warm-starts-and-sessions) | boolean | hosted: yes | `false` only in lenient non-hosted profiles | | [`cr_enabled`](#state-warm-starts-and-sessions) | boolean | deprecated compatibility field | ignored for runtime selection | | [`settlement_mode`](#state-warm-starts-and-sessions) | enum | no | `standard` | | [`session.idle_timeout`](#state-warm-starts-and-sessions) | duration | yes | — | | [`session.max_lifetime`](#state-warm-starts-and-sessions) | duration | yes | — | | [`resources.cpu`](#resources) | quantity | yes | — | | [`resources.memory`](#resources) | quantity | yes | — | | [`resources.cpu_limit`](#resources) | quantity | no | platform burst default | | [`resources.memory_limit`](#resources) | quantity | no | platform burst default | ### Network, policy, tools, and delegation | Field | Type | Required | Default | |---|---|---|---| | [`network.egress`](#network) | list of string | no | empty | | [`network.egress_mode`](#network) | `allowlist` or `public` | no | `allowlist` | | [`policy.*`](#policy) | block | no | see the section | | [`integrations`](#governed-mcp-connectors) | map of requirement | no | empty | | [`access.github`](#github-repositories) | block | no | none | | [`delegation.*`](#agent-to-agent-delegation) | block | no | empty | ### Metadata, caps, and harness | Field | Type | Required | Default | |---|---|---|---| | [`data_classification`](#declared-capability-metadata) | list of string | no | empty | | [`cloud_access`](#declared-capability-metadata) | list of string | no | empty | | [`cost_cap_usd`](#declared-capability-metadata) | number | no | `0` | | [`token_cap`](#declared-capability-metadata) | integer | no | `0` | | [`lifecycle_policy`](#declared-capability-metadata) | block | no | empty | | [`harness.`](#harness-settings) | block | no | harness defaults | | [`autonomy`](#declared-capability-metadata) | any | no | accepted, not yet applied | ## Identity and packaging ```yaml name: support-copilot version: 0.1.0 framework: claude-agents-sdk entrypoint: agent:create_agent description: Triage and draft responses to support requests. ``` - **`name`** — the stable agent identity, and the name every other command refers to. Must start with a letter or digit and contain only letters, digits, underscore, dot, or dash, up to 128 characters. A deploy that names a different agent than the manifest does is rejected. - **`version`** — the label for one immutable build. Validation does not require it, but deploy does: supply it here or pass `--version` on the command line. A published version is immutable, so publish a new label rather than rebuilding an existing one. - **`framework`** — the harness adapter. One of `claude-agents-sdk` or `deepagents`; confirm the set your control plane offers with `tanomis agent harnesses`. - **`entrypoint`** — `module:function`, resolved inside your packaged project. Exactly one colon, with both halves non-empty. - **`description`** — free text shown in listings. Optional, and worth writing: it is what a person scanning the agent catalog reads. ## Behavioral classification ```yaml kind: interactive ``` **`kind`** is required and is one of: | Value | Meaning | |---|---| | `interactive` | Human in the loop, turn by turn. Latency-sensitive resource profile. | | `background` | Runs without a person waiting on each turn. | **`autonomy`** is accepted and stored verbatim but is not yet applied to behavior. Set it only if you want the declaration on record. ## Models ```yaml model: "your-workspace-model-id" permitted_models: - "your-workspace-model-id" ``` - **`model`** — the default model resolved for a run. `default` follows the organization's default model, resolved at each session start; any other value pins that model. - **`permitted_models`** — the runtime allowlist. A run that requests a model outside this list is refused. Leaving it empty does not widen access; it means the manifest declares no allowlist and the model decision falls to platform and tenant policy. Use model IDs available to the selected tenant. Whenever you change `model`, update the matching `llm::invoke` entry in `policy.scope_ceiling`, or the declared ceiling stops describing the model the agent actually calls. ## Attribution and identity ```yaml identity_mode: service inherits_user_permissions: false ``` - **`identity_mode`** — the agent's intended authority posture. One of `service` (the agent's own machine identity), `delegated` (intended user subject plus agent actor), or `hybrid`. Hosted deployments require an explicit value and reject omission; a lenient non-hosted profile may resolve a missing value to `service`. **The first non-rejected version pins it** across every later version of the agent; a rejected initial attempt does not establish the pin, so agree the mode with your organization administrator before the first deploy. - **`inherits_user_permissions`** — the gated impersonation opt-in. Illegal with `identity_mode: service`, because there is no user to inherit from; use `delegated` or `hybrid`. Turning it on seeds an impersonation-approval gate rather than granting anything on its own. - **`identity.client_id`** — the agent's application identity. On a hosted control plane this is assigned at registration and an author-supplied value is **rejected**, so omit the `identity` block entirely. > **Availability:** Service-mode bind-time identity is enabled per tenant; delegated and scoped authority are held contracts. See [What is available where](/reference/availability). For the actor model, hybrid-path boundary, denial sequence, and safe attribution guidance, read [Agent identity and access](/concepts/identity-and-access). ## State, warm starts, and sessions ```yaml persistent: true session: idle_timeout: 30m max_lifetime: 24h ``` - **`persistent`** — provisions durable tenant-scoped storage and gives each session its own directory. It is required for new hosted versions. - **`cr_enabled`** — deprecated. Older manifests may retain it during the internal rolling deployment, but true, false, and omission do not select a runtime in current runtime-api. The deploy pipeline and CLI still use `true` temporarily to request a golden build; that legacy consumer is removed by the golden-policy follow-up. RAM snapshot policy is platform-owned. Until universal-worker and golden-policy cutover, remote admission requires `true` for settlement or author-declared egress; that is a rollout barrier, not a tenant-facing compatibility period. - **`settlement_mode`** — one of `standard`, `work`, or `settlement`, in that order of strength; a missing value resolves to `standard`. `standard` is the existing behavior. `work` means the agent produces durable work records. `settlement` requires the session to establish the managed worker/proxy boundary or fail activation. - **`session.idle_timeout`** / **`session.max_lifetime`** — both required. Compact Go-style durations (`45s`, `30m`, `24h`), positive, and resolving to whole seconds. `1500ms` is rejected. > **Availability:** A snapshot is an optimization, not the execution-boundary selector. Durable work and settlement are enabled per tenant; a settlement-mode session fails activation where the boundary is unavailable. See [What is available where](/reference/availability). ## Resources ```yaml resources: cpu: 100m memory: 512Mi cpu_limit: 2 memory_limit: 2Gi ``` `cpu` and `memory` are the **requests** — the reserved baseline that drives scheduling and density. They are required. `cpu` accepts a Kubernetes CPU quantity (`500m`, `2`); `memory` accepts a binary quantity (`512Mi`, `2Gi`) — decimal suffixes like `500M` are rejected. `cpu_limit` and `memory_limit` are the optional **burst ceiling**. When unset the platform applies a generous default so a run can spike above its request. Set them to raise or pin the ceiling for one agent, subject to tenant policy. Leave the requests at their scaffolded values until a measurement gives you a reason to change them. ## Network ```yaml network: egress: - github.com - api.example.com ``` **`network.egress_mode`** defaults to `allowlist`. Set it to `public` to permit arbitrary public hostnames through the managed proxy. In public mode, `egress` must be absent or empty. An unset tenant ceiling permits this mode; any configured hostname ceiling rejects it. Private and reserved addresses remain blocked. Public mode requires capable node/helper versions and the external route; see [Restrict network egress](/guides/govern/restrict-network-egress#allow-arbitrary-public-websites). **`network.egress`** is the list of external hostnames this agent may reach. It is part of the version's contract: captured at registration, immutable for that version, and enforced at run time. Availability is per tenant. Ask your administrator whether author-declared egress is enabled for the selected tenant before relying on it; where it is not, the list is still validated and recorded but no external route exists. ### What an entry may be Each entry is a bare hostname, written in canonical form — lowercase, no surrounding whitespace, no trailing dot. Anything else is refused at deploy rather than quietly rewritten, so the value you wrote is the value that is enforced. Refused, each with the reason named: | Not allowed | Why | |---|---| | `*.github.com` | Wildcards are not permitted in a declared ceiling. An entry already covers its subdomains. | | `https://github.com`, `github.com:443`, `github.com/path` | The match is on host alone; a scheme, port, or path would be silently ignored. | | `140.82.121.4` | An address carries no DNS identity, and the allowlist matches the host an agent asks for. | | the loopback name, `svc.cluster.local`, `printer.local`, `vault.internal` | Names for the sandbox itself or the in-cluster world. Platform destinations are added for you and are not declarable. | | `GitHub.com`, `github.com.` | Not canonical. Write `github.com`. | | `github` | A hostname needs at least two labels. | **An entry covers its subdomains.** `github.com` admits `api.github.com` and `a.b.github.com`. It does not admit `notgithub.com` or `github.com.example`. Declare the narrowest name that covers what you need. ### The tenant ceiling Your administrator can set a per-tenant ceiling that your declaration is intersected with. It only ever narrows: it cannot add a destination you did not declare, and it never affects the platform's own routes. An unconfigured tenant admits any public hostname, so an unset ceiling behaves as though only your manifest applied. Where a ceiling excludes an entry, the deploy fails naming both the entry and the ceiling that excluded it, before any image is built — so you find out in seconds rather than after a build. A ceiling of `github.com` admits a declaration of `api.github.com`, because that is narrower than what the administrator permitted. ### What is enforced, and where - **At deploy.** The grammar and the tenant ceiling, before a build is created. - **At rollout.** The ceiling again, when the version is enabled for a tenant — the point at which a globally published agent first has a tenant to be measured against. - **At run time.** Every connection is mediated. A destination outside the list is refused. A declared hostname that resolves to a private, loopback, link-local, or in-cluster address is refused too, and the connection is made to the address that was checked, so a name whose record changes cannot redirect it. The platform's own destinations — the managed model gateway and the governed connector routes — are always available and are never declared here. Your list can only add to them. ### Requirements Egress enforcement runs in the managed worker's session-scoped proxy for both snapshot restore and clean bootstrap. If that boundary cannot be established, activation is refused instead of running unproxied. During the internal mixed-runtime rollout only, remote admission may require the legacy `cr_enabled: true` spelling so an older runtime replica cannot accept a contract it cannot enforce. ## Policy ```yaml policy: approval_required_tools: - Bash enable_ask_user: false enable_claude_md: false enable_context_files: false scope_ceiling: - "llm:your-model-family:invoke" allowed_audiences: - "https://your-workspace-api.example" approval_thresholds: - condition: "scope:payments.*" approver: workspace-admin ``` ### Human-in-the-loop - **`approval_required_tools`** — every listed tool pauses the run and emits a permission request that the console renders as an allow/deny card. Names are the harness's own tool names. - **`enable_ask_user`** — exposes the harness question flow, letting the agent ask a person a question mid-run and wait for the answer. - **`enable_claude_md`** — `claude-agents-sdk` only. Lets the agent read a project `CLAUDE.md` from its workspace as a context file. Ignored on other harnesses. - **`enable_context_files`** — accepted for forward compatibility with a broader context-file surface. Today the only such surface is gated by `enable_claude_md`. `approval_required_tools` and `enable_ask_user` produce the durable runtime interactions described in [Handle approval-required actions](/guides/run/approval-required-actions). That application flow is separate from `approval_thresholds` below, whose scoped-token completion path remains held. ### Authorization contract - **`scope_ceiling`** — the maximum set of concrete capabilities this agent's tokens may ever carry. The broker intersects requested scopes with it; it never widens them. Each entry must match exactly one of three grammars, and **wildcards are rejected**: | Form | Example | |---|---| | `.` | `calendar.read` | | `mcp:::` | `mcp:github:create_issue:write` | | `llm::` | `llm:your-model-family:invoke` | On a hosted control plane the ceiling is required; where it is optional, an absent ceiling means allow-all with a warning. **What the ceiling governs.** It bounds what this agent may do *acting as itself*, in two places: - **Agent-to-agent delegation.** On a delegated call the caller's ceiling is intersected with the callee's, and only scopes inside both are granted. An agent with no ceiling cannot receive a delegated grant at all. - **Token exchange (held contract).** If a future supported flow exchanges a token against one of the agent's `allowed_audiences`, a requested scope outside the ceiling is refused. No supported public runtime workflow currently exposes this scoped exchange. **What it does not govern.** Three settings look like "what may this agent do", and they are separate mechanisms — sizing the ceiling for these will not work: | Concern | Where it is actually enforced | |---|---| | Which model a run may use | `permitted_models` | | Which MCP tools an agent may call | `integrations..tools.allow` intersected with the current workspace grant | | What the agent may do as itself | **`scope_ceiling`** | In particular, an `mcp:::` entry is a well-formed ceiling scope, but adding one does **not** grant an MCP tool call and omitting one does not block it — see [Governed MCP connectors](#governed-mcp-connectors). Entries are captured at registration and are immutable for that version, so declare the ceiling you need and widen it in a later version rather than padding it defensively. - **`allowed_audiences`** — the `https://` resource indicators the agent's minted tokens may target. **Required once `scope_ceiling` is non-empty**, and each entry must be an `https://` URI. Use the audience issued for your tenant/environment; never copy one from another environment. - **`approval_thresholds`** — maps a sensitivity condition to an approver. In the held delegated exchange contract, matching a sensitive scope classifies the request as approval-required; no supported durable approval ID, decision endpoint, or resume path currently completes that exchange. `condition` is required and is a sensitivity *pattern*, so unlike a grant it may use wildcards. `approver` is optional and defaults to the invoking user. See [Agent identity and access](/concepts/identity-and-access#what-the-service-assertion-is-and-is-not) before designing around these fields. Both `scope_ceiling` and `allowed_audiences` are re-checked for presence on each new version, so a new version can widen them — which is exactly the decision an agent's author should be making deliberately. ## Declared capability metadata ```yaml permitted_tools: [] data_classification: - internal cloud_access: - object-storage cost_cap_usd: 25 token_cap: 2000000 lifecycle_policy: required_gates: - regression-suite - prompt-injection-suite gate_categories: prompt-injection-suite: adversarial rollback: manual ``` - **`permitted_tools`** — the tool allowlist carried into the policy decision point. An empty list declares no manifest-level restriction; enumerate tools to narrow what the agent may run. - **`data_classification`** and **`cloud_access`** — the data sensitivity and cloud-resource classes the agent declares. Recorded on the version and surfaced through the catalog for review. - **`cost_cap_usd`** and **`token_cap`** — declared budgets for the version. Must not be negative. - **`lifecycle_policy.required_gates`** — the evaluation gates that must pass before the gate-guarded promotion. **`lifecycle_policy.rollback`** is `manual` or `auto`; empty resolves to `manual`. - **`lifecycle_policy.gate_categories`** maps a required gate to the kind of eval suite behind it: `quality`, `robustness`, or `adversarial`. A gate left out is `quality`. Each key must name a gate in `required_gates`, and any other value is refused at registration. The version's gate listing (`GET /v1/agents/{name}/versions/{version}/gates`) returns the category on every gate so robustness and adversarial suites can be shown apart from quality evals. `data_classification`, `cloud_access`, `cost_cap_usd`, and `token_cap` are recorded as version metadata and exposed for governance review. Treat them as the agent's declaration of intent, and keep the enforcement in `permitted_models`, `permitted_tools`, and `policy.scope_ceiling`. ## Governed MCP connectors An agent references a configured connector directly. No catalog association, contract publication, or connection-selection step is needed. ```yaml title="agent.yaml" integrations: finance: connector: global/alphavantage-workspace required: true tools: allow: [SYMBOL_SEARCH, COMPANY_OVERVIEW, EARNINGS] ``` Find the connector and its approved tools: ```bash tanomis mcp connector list tanomis mcp connector tools CONNECTOR_ID ``` Use `global/NAME` for a platform connector, `tenant/NAME` for a connector owned by the agent's workspace, or an exact connector ID. An unqualified name works when unambiguous. A global agent can reference global connectors; publish a tenant-scoped agent when it needs a tenant-owned connector. The integration map key is just the agent's local name. The connector supplies the endpoint, credentials and shared/personal account mode. Personal connectors still require each caller to connect their own account. Credentials never belong in the manifest. An optional `identity: workspace` or `identity: user` can assert the expected mode; it must match the connector. `tools.allow` is the maximum set the agent may use. Optional `tools.approval_required` lists tools that require confirmation for each call and must be a subset of `tools.allow`. The connector's current permitted tools and account access can narrow this set, never expand it. At registration, the platform records the connector ID and approved tool schemas automatically. These platform-owned details appear separately from author configuration in the Manifest tab. Deleting and recreating a connector with the same name does not redirect an existing version. ## GitHub repositories An agent names the GitHub repositories it may use, and what it may do in each, under `access.github`. Each run gets a GitHub token for exactly those repositories and capabilities; `git` and `gh` use it automatically. ```yaml title="agent.yaml" access: github: repos: anyfind/agent-platform-website-tanomis: can: [read, push, pull_request] anyfind/docs: can: [read] ``` | Capability | Lets the agent | GitHub App permission | |---|---|---| | `read` | clone, fetch, read files and metadata (always included) | contents: read | | `push` | push branches | contents: write | | `pull_request` | open and update pull requests | pull requests: write | | `issues` | create and comment on issues | issues: write | There is no separate merge capability: `push` and `pull_request` together can merge a pull request unless branch protection forbids it. A global agent cannot know a customer's repositories, so it declares a named slot instead. Each workspace chooses the repository for the slot when it enables the agent (**Agent → Integrations**, or `tanomis agent access NAME --bind target=owner/name`): ```yaml title="agent.yaml" access: github: choose: target: can: [read, push, pull_request] ``` What decides access: - **The tenant's GitHub ceiling.** A workspace admin sets which repositories agents may be given, and how far, in **Settings → Organization → GitHub** (or `tanomis github ceiling set`). A declaration outside the ceiling is refused at deploy or enablement with the gap named. Lowering the ceiling later makes affected runs fail with the same reason. - **The GitHub App installation.** A repository the workspace's connected installation cannot see is refused at deploy. - **Every run.** The intersection is checked again when a run starts and on every token request, and tokens are revoked when the run ends. Rules: - Repository names are GitHub `owner/name`; `owner/*` is a ceiling pattern, not a declaration. - Slot names use lowercase letters, digits, `-` and `_`. - `github.com` and `api.github.com` are added to `network.egress` automatically. That opens those hosts for every path: the run's token limits what the agent can do on GitHub with platform credentials, not which GitHub repositories it can send data to. - `asks_first` is not supported yet and is rejected rather than ignored. - The retired top-level `repository:` block is rejected with a rewrite hint, including when an older version that carries it is cloned or fanned out. See [GitHub repositories](/guides/connect-tools/github-repositories) for connecting GitHub and setting the ceiling. ## Changes and availability - Changing the connector or allowed tools requires a new agent version. - Rotating credentials on the same connector does not require a new version. - Changed tool schemas require review and a new agent version. - **Agent → Integrations** shows the declared connector, readiness and expandable allowed tools. Operators can disable or enable access for the agent; they cannot substitute another connector. - A required connector that is unavailable blocks execution. An unavailable optional connector is omitted. - Disabled access remains disabled when publishing another version or migrating an old declaration. Existing catalog-based agent versions remain compatible. New agents should use the direct connector declaration above. No attachment creation or approval workflow is involved. ## Agent-to-agent delegation ```yaml # caller delegation: calls: - summarizer - agent: reconciler skills: [reconcile] # callee delegation: accepts: - tenant skills: # optional - name: summarize description: Summarize a support thread and return the key facts and the customer's ask. ``` The block has two independent halves. An agent may call, be called, both, or neither, and declaring neither is the closed default. **The manifest is the grant:** a caller names the agents it may hand work to, a callee names who may call it, and the intersection of the two, evaluated against the versions currently enabled for the tenant, is the authority. Nothing is created out of band and nothing has to be redone when either side deploys a new version. - **`calls`** — the agents this agent may delegate to. Naming a target puts the delegation tools on this agent's bind, and it is the caller-side ceiling: an agent cannot reach a callee it did not name. An entry is a bare agent name, or a mapping with `agent` and a `skills` list that narrows the target to those skills. No wildcards. - **`accepts`** — who may delegate to this agent. Empty means nobody. | Value | Meaning | |---|---| | `tenant` | Any agent in the same tenant. The widest class there is. | | `self` | Other versions of this same agent only. | | `` | That one named agent. | - **`skills`** — optional. A callee that declares none exposes one implicit skill, `default`, whose input is a `prompt` plus any further fields the caller passes, described by the agent's own `description`. Declare skills only when the agent offers several distinct typed operations. `name` is lowercase alphanumeric with underscores or dashes and unique within the list; renaming one breaks every caller that lists it. `description` is required and functional, not decoration: a calling model reads it to choose a skill. `input_schema` is optional; when present it is enforced on every call. **Schema hashes are derived by the CLI, never written by hand.** `skills` without `accepts` is rejected: the skills would be unreachable. `accepts` without `skills` is fine and is the common case. ## Harness settings Keep only the block matching `framework`. Unknown keys inside a harness block are rejected at warmup with the offending key named. ### `claude-agents-sdk` ```yaml harness: claude-agents-sdk: {} ``` The Claude Agent SDK harness takes no manifest options today; the empty block is the marker. Its human-in-the-loop behavior comes from `policy.approval_required_tools`, `policy.enable_ask_user`, and `policy.enable_claude_md`. ### `deepagents` ```yaml harness: deepagents: is_async: true stream_modes: [messages, updates, custom] checkpointer: workspace-sqlite backend: workspace-composite recursion_limit: 50 ``` | Key | Values | Default | |---|---|---| | `is_async` | boolean | `true` | | `stream_modes` | any of `messages`, `updates`, `custom`, `values` | `[messages, updates, custom]` | | `checkpointer` | `workspace-sqlite`, `in-memory` | `workspace-sqlite` | | `checkpointer_options.delta_channels` | boolean | `true` | | `store` | `workspace-sqlite`, `none` | `none` | | `backend` | `workspace-composite`, `author-provided` | `workspace-composite` | | `allow_agent_model_override` | boolean | `false` | | `allow_author_checkpointer` | boolean | `false` | | `allow_author_store` | boolean | `false` | | `recursion_limit` | positive integer | `200` | | `recursion_limit_ceiling` | positive integer | `1000` | | `enable_repl_middleware` | boolean | `false` | | `subagent_event_attribution` | `nested`, `flat` | `nested` | `recursion_limit` must be less than or equal to `recursion_limit_ceiling`. The runtime clamps the effective limit to the ceiling, so the ceiling is the real bound on a single run's step count. Leave `backend`, `checkpointer`, and the three `allow_author_*` switches at their defaults unless you are deliberately taking ownership of workspace state; the defaults are what keep the workspace and lifecycle under the runtime's control. ## Cross-field rules These are the checks that reject a manifest which is individually well-formed: - New hosted versions require `persistent: true`. - During the internal mixed-runtime rollout only, remote admission may require legacy `cr_enabled: true` for settlement or author-declared egress. The manifest validator itself does not make that field authoritative. - `inherits_user_permissions: true` is illegal with `identity_mode: service`. - A non-empty `policy.scope_ceiling` requires a non-empty `policy.allowed_audiences`. - `integrations..tools.approval_required` must be a subset of that integration's `tools.allow`. - `delegation.skills` requires a non-empty `delegation.accepts`. - `harness.deepagents.recursion_limit` must not exceed `recursion_limit_ceiling`. ## Validate before you build ```bash tanomis agent deploy . --tenant northwind-sandbox --dry-run ``` `--dry-run` runs the full preflight, including the authorization contract, and packages locally without creating a build or uploading anything. Use it: on the remote build path, registration is the **last** step, so a manifest error found there costs you an image that has already been built and pushed. --- Source: /reference/availability # What is available where Which capabilities are on for every tenant, which are enabled per tenant, and how to tell from the API or CLI before you build on one. Most of Tanomis is on for every tenant. A few capabilities are enabled per tenant by a platform operator, either because they need tenant-side configuration or because they are rolling out shadow-first. This page is the one place that says which is which, so the guides do not have to. - **Hosted Tanomis:** the per-tenant capabilities are enabled on request; ask us. - **Self-hosted:** your platform operator enables them per tenant. - Where a capability has a probe, check it in code rather than assuming. ## Capabilities | Capability | Hosted Tanomis | Self-hosted | How to tell | |---|---|---|---| | Sessions, runs, streaming, replay | On | On | Always present. | | Durable approvals and questions | On | Operator enables | A paused run shows `awaiting_input: true`; see [Approval-required actions](/guides/run/approval-required-actions). | | Session files | On | Operator enables | `file_attachments_enabled` on [Inspect the caller](/api/workspaces/whoami), then `session_files_supported` on the session. | | Warm starts (golden snapshots) | On | On | The deploy's final golden line. A skipped golden still deploys; sessions cold-start. | | Automation credentials | On | On | `tanomis m2m ls`; see [Automation credentials](/guides/deploy/automation-credentials). | | Author-declared network egress | Per tenant | Operator enables | Without it the list is validated and recorded but no external route exists. See [Restrict network egress](/guides/govern/restrict-network-egress). | | MCP connectors and personal OAuth | Verified in SaaS development; platform enablement required | Operator enables and configures | Agent → Integrations shows connection and account readiness. Not enabled by default in every deployment. See [MCP connectors](/guides/connect-tools/mcp-connectors). | | Manifest-declared connector access | Enabled for Acme development | Other tenants require explicit enablement | The manifest fixes the connector and allowed tools; the platform records tool schemas automatically. Operators can enable or disable access, but cannot substitute another connector. Explicit disables persist across versions. See [Declare an integration](/guides/connect-tools/integration-contracts). | | Custom connectors | Per tenant | Operator enables | Needs the `manage` capability in your workspace. See [Create a custom connector](/guides/connect-tools/create-a-custom-connector). | | Delegation between agents | Per deployment | Operator enables | The delegation tools are present in the bind of an agent whose manifest names a target under `delegation.calls`. See [Delegate work to another agent](/guides/run/delegate-to-another-agent). | | Durable work and settlement | Per tenant | Operator enables | A `settlement`-mode version fails session activation where the boundary is unavailable. See [Durable work and settlement](/guides/run/durable-work-and-settlement). | | Evaluation and the rollout gate | Per tenant, shadow-first | Operator enables | The gate records a verdict before it is allowed to block. See [Evaluation](/guides/evaluate/overview). | | Metering and budgets | Per tenant | Operator attaches the decision point | A session shows usage as not measured where it is off. See [Usage and spend](/guides/govern/usage-and-spend). | | Service-mode agent identity | Per tenant | Operator enables | No public probe; the session runs without an identity assertion where it is off. See [Identity and access](/concepts/identity-and-access). | | Scoped and delegated agent authority | Held | Held | Declarations validate and record; no public runtime workflow uses them yet. | "Per tenant" means the capability exists in the platform and a platform operator turns it on for a tenant. "Held" means the contract is defined and the runtime path is not yet offered. ## Coming soon Pages marked **Soon** in the sidebar describe capabilities that are being built. They carry an under-construction notice and change without a changelog entry until they ship. ## Where to go next - [Changelog](/changelog): when a capability moves from per tenant to on. - [Get access](/getting-started/get-access): how to ask for a per-tenant capability on hosted Tanomis. --- Source: /reference/cli-configuration # CLI configuration Where tanomis keeps its configuration, how contexts and credentials are stored, and the environment variables and flags that override them. `tanomis` resolves its configuration from three layers: flags on the command, environment variables, and a config file with named contexts. Flags win, then the environment, then the file. ## The config file `tanomis` searches from the working directory upward for `.tanomis.yaml`, then `.cog.yaml` at each level. If no repo config exists, it uses `~/.config/tanomis/config.yaml`, falling back to `~/.config/cog/config.yaml` when the new file is absent. `--config PATH` selects an explicit file. Configuration and cached credentials migrate to the new directory when each is next written; explicit file paths are respected. `tanomis login` creates a context. ```yaml title="~/.config/tanomis/config.yaml" current-context: saas contexts: saas: auth-mode: oidc # endpoints discovered from the hosted console at login; no secret here ``` A context holds the environment's endpoints, its authentication mode, and the selected workspace. It never holds a secret: cached credentials live separately, per context, and `tanomis logout` clears them while keeping the context. ## Contexts ```bash title="Terminal" tanomis config current-context tanomis config use-context staging tanomis config view tanomis config delete-context old ``` `view` prints the effective configuration after flags and environment are applied, which is the fastest way to see which environment a command will hit. Create a context by logging in to that environment with `tanomis login --console URL --name NAME`. ## Authentication modes | Mode | Used for | |---|---| | `oidc` | The hosted device-authorization login a person completes in a browser. The hosted default. | | `none` | Environments with no authentication; not applicable to hosted Tanomis. | For unattended use, `tanomis` exchanges an automation client's ID and secret for a token when `TANOMIS_API_URL`, `TANOMIS_CLIENT_ID`, and `TANOMIS_CLIENT_SECRET` are set; no login mode is involved. See [Automation credentials](/guides/deploy/automation-credentials). For human logins, access tokens are short-lived. Before an authenticated command, `tanomis` reuses a valid token or refreshes it; if the refresh grant is gone, the command stops and asks you to `tanomis login` again. ## Environment variables | Variable | Effect | |---|---| | `TANOMIS_API_URL` | Public platform API origin for machine authentication and discovery. | | `TANOMIS_CLIENT_ID` | Organization automation client ID. Use with `TANOMIS_CLIENT_SECRET`. | | `TANOMIS_CLIENT_SECRET` | Automation client secret, injected from a secret manager. Never saved by machine login. | | `TANOMIS_CA_BUNDLE` | CA bundle path for deployments with a private certificate authority. | | `TANOMIS_TENANT` | Default for `--tenant` on tenant-scoped commands. | | `TANOMIS_OUTPUT` | Default for `--output`: `table` or `json`. | Each `TANOMIS_*` CLI setting accepts its former `COG_*` name as a fallback when the new name is unset; using an old name emits one deprecation notice per process. Other `TANOMIS_*` variables exist for internal endpoint and identity plumbing and are not part of the public contract. With a human login, deploy and session-run commands refuse a `--tenant` that differs from the selected workspace, so the variable and the selection must agree. Machine authentication ignores cached human workspace and support context; choose an organization tenant explicitly, or let the sole/default ready tenant be selected. ## Global flags | Flag | Effect | |---|---| | `--context NAME` | Use a context for this command without changing the current one. | | `--tenant ID` | Tenant for tenant-scoped commands. Must match the selected workspace where one is selected. | | `-o, --output table\|json` | Output format. Use `json` in scripts. | | `--config PATH` | Config file path. | | `--verbose` | Verbose output. | Every command page in the [CLI reference](/cli) lists these under Global flags. ## Where to go next - [Authenticate and select a workspace](/getting-started/authenticate-and-select-workspace) - [Environments and contexts](/guides/deploy/environments-and-contexts) --- Source: /reference/errors # Errors reference Every error code the Platform API and the CLI can return, with its HTTP status and what to do about it. Errors are JSON with a machine-readable `error` code and usually a human-readable `message`. Clients must tolerate a code-only response from the access gate. ```json { "error": "validation_error", "message": "idempotency_key exceeds 255 bytes" } ``` Handle by status first, then refine by code. The status tells you whether to retry; the code tells you what to change. ## By status | Status | Retry? | Typical codes | |---|---|---| | `400` | No, fix the request | `bad_request`, `validation_error`, `invalid_request`, `invalid_json`, `missing_tenant`, `invalid_tenant`, `unknown_tenant`, `workspace_not_selected`, `bad_limit`, `bad_path`, `missing_path` | | `401` | With a new token | `unauthorized` | | `403` | No, change the grant or operation | `forbidden`, `direct_operator_required` | | `404` | No, check ids and capabilities | `not_found`, `no_active_bundle` | | `405` | No | `method_not_allowed` | | `409` | Read the resource, then decide | `conflict`, `work_has_live_run`, `upload_in_progress`, `invalid_revision`, `not_rejected`, `fact_schema_mismatch` | | `412` | No, create a replacement | Idempotent create against a terminal session | | `422` | No, fix the request | `validation_error`, `unprocessable`, `over_budget`, `incomparable`, `empty_dataset_version` | | `429` | Yes, with backoff and jitter | Rate limited | | `502` | Bounded retry | `upstream_error`, `registry_unavailable`, `account_service_unavailable` | | `503` | Bounded retry | `unavailable` | | `500` | Bounded retry, then report | `internal_error`, `lookup_failed`, `read_failed`, `list_failed`, `validate_failed`, `audit_unavailable` | ## By code | Code | Meaning | What to do | |---|---|---| | `bad_request` | The request could not be understood. | Fix the request shape. | | `validation_error` | A field failed validation; `message` names it. | Correct the field. Sent as `400` for request shape and `422` for semantic checks such as a manifest rule. | | `invalid_request` | The combination of fields is not allowed, such as `work.subject` with `work.work_id`. | Send one or the other. | | `invalid_json` | The body is not valid JSON. | Fix the encoding. | | `missing_tenant`, `invalid_tenant`, `unknown_tenant` | No tenant resolved, or the header named one the token does not grant. | Send the workspace headers from `GET /v1/me/workspaces`. | | `workspace_not_selected` | The CLI has no selected workspace for this context. | `tanomis workspace use ORG --tenant TENANT`. | | `workspace_not_armed` | The selected workspace is not usable yet, typically a tenant still being provisioned. | Wait for the tenant to be `ready`. | | `unauthorized` | Missing, expired, or invalid access token. | Obtain a new token. | | `forbidden` | The caller cannot perform this operation in the selected workspace. | Ask an owner for the capability or grant. | | `direct_operator_required` | The operation is reserved for a Tanomis operator acting directly. | Contact your administrator. | | `not_found` | The resource is absent in the selected tenant, or an optional capability is unavailable here. | Check the id, the workspace, and the capability. | | `no_active_bundle` | The tenant has no active policy bundle for this decision. | Contact your administrator. | | `conflict` | A lifecycle or idempotency conflict: another run is active, the key matched a different body, or the session cannot admit work. | Read the resource, then retry or create anew. | | `work_has_live_run` | The settlement work already has a live run. | Wait for it to finish. | | `upload_in_progress` | The file's bytes are still being transferred. | Wait, then retry. | | `invalid_revision` | The policy revision you referenced is not current. | Reload and resubmit. | | `not_rejected` | The transition needs a rejected version and this one is not. | Check `tanomis agent statuses`. | | `over_budget` | The evaluation run would exceed its example budget. | Lower `--max-examples`. | | `incomparable` | The two experiments do not share a dataset version. | Compare experiments over the same dataset version. | | `empty_dataset_version` | The dataset version has no examples. | Populate it before running. | | `unavailable` | The capability or a dependency is temporarily unavailable. | Retry with backoff. | | `upstream_error`, `registry_unavailable`, `account_service_unavailable` | A platform dependency failed. | Retry boundedly; report if it persists. | | `internal_error` and other `*_failed` codes | An unexpected platform error. | Retry boundedly, then contact support with the request id. | ## CLI The CLI prints the server's code and message as returned and exits non-zero. It forms no authorization opinion of its own: a refusal you see at the terminal is the server's, so a rule change never leaves a stale client refusing correct calls. Local refusals are limited to missing arguments and to irreversible commands that require `--yes`. ## Where to go next - [Platform API overview](/api#errors-and-retries): the retry contract per status. - [Sessions and runs](/concepts/sessions-and-runs): the conflicts you can hit on create and run. --- Source: /reference/events # Events reference The envelope every run event shares, the event types a consumer can rely on, and the payload of each. Run events are how your application watches a run. They arrive as server-sent events on `GET /v1/sessions/{session_id}/runs/{run_id}/events`, each frame carrying one JSON event. - Every event has the same envelope: `id`, `type`, `session_id`, `run_id`, `created_at`, `data`. - Three types are **terminal**: `run_completed`, `run_cancelled`, `error`. - Two types mean **a person is needed**: `permission_request` and `user_question`. - Everything else is progress. Ignore unknown types and unknown fields; they are forward compatible and non-terminal unless this page names them. ## The envelope ```text id: 14 event: message_delta data: {"id":"14","type":"message_delta","session_id":"ses_7d4f9c","run_id":"run_a12bc3","created_at":"2026-09-03T09:00:00.014Z","data":{"text":"I found"}} ``` | Field | Meaning | |---|---| | `id` | Orders events within this run only. Opaque; store it with the session and run ids, and send it back as `Last-Event-ID` to resume. | | `type` | The event type below. | | `session_id`, `run_id` | The run the event belongs to. Deduplicate by run id plus event id. | | `created_at` | RFC 3339 UTC. | | `data` | The type-specific payload. | ## Lifecycle events | Type | Terminal | Payload | Meaning | |---|---|---|---| | `run_started` | No | `run_id` | The run was dispatched to the agent. | | `run_completed` | **Yes** | `run_id`, optional `recovered` | The run finished. Read the run resource for its authoritative status. | | `run_cancelled` | **Yes** | `run_id`, optional `recovered` | The run ended after cancellation. | | `error` | **Yes** | `message`, and an error code where one applies | The run ended with an error. The run resource carries `error_code` and `error_message`. | `recovered: true` marks a terminal event emitted while the platform reconciled a run after a restart, rather than by the agent process itself. Treat it exactly like the non-recovered form. ## Output events | Type | Payload | Meaning | |---|---|---| | `message_delta` | `text`, `framework`, `model` | A chunk of the agent's response. Concatenate deltas in id order. | Harness-specific events can appear alongside these, with their own payloads. Render what you recognise and ignore the rest. ## Interaction events | Type | Payload | Meaning | |---|---|---| | `permission_request` | `request_id`, `interaction_key`, `tool_name`, `tool_input`, optional `replayed` | The agent wants to run a tool listed in `approval_required_tools` and is waiting for a decision. | | `permission_resolved` | `interaction_key` and the decision | A decision was recorded. | | `user_question` | `request_id`, `interaction_key`, the question, optional `replayed` | The agent asked a person a question and is waiting. | | `user_answered` | `interaction_key` and the answer | An answer was recorded. | Answer by `interaction_key`, never by `request_id`, and never from the event alone: list the session's pending interactions to get the durable record, then answer it. `replayed: true` marks an interaction re-emitted after a reconnect or a restored runtime; it is the same interaction, not a new one. See [Approval-required actions](/guides/run/approval-required-actions). ## Context events | Type | Meaning | |---|---| | `context_file_loaded`, `context_file_changed` | A context file was read into the agent's context, or changed since. | | `claude_md_loaded`, `claude_md_changed` | The same, for a project `CLAUDE.md` when `enable_claude_md` is on. | | `agent_skills_loaded` | The agent's `skills/` directory was mirrored into the session workspace. Names the skills that loaded, and warns about directories that could not. | ## Rules a consumer can rely on 1. Ids order events within one run and are opaque strings. Never compare ids across runs. 2. Replay after `Last-Event-ID` starts strictly after that id when history is available. Store the id after processing the event, and deduplicate by run id plus event id. 3. A stream can close cleanly without a terminal event. Read the run resource, and reconnect only while it is non-terminal. 4. The three terminal names above are the contract. Harness-specific catalogs and payload fields can evolve; the envelope, the cursor behaviour, and the terminal names do not. ## Where to go next - [Stream and replay events](/guides/run/stream-and-replay-events): the consumer loop with cursor storage and deduplication. - [Stream run events](/api/runs/stream-run-events): the endpoint. --- Source: /reference/glossary # Glossary The words Tanomis uses, each defined once, with a link to the page that explains it. | Term | Meaning | |---|---| | **Agent** | A stable name that owns a lineage of versions. [Agents and versions](/concepts/agents-and-versions) | | **Automation client** | An organization-owned machine identity for CI or a backend, with a permission profile; also called an M2M client. [Automation credentials](/guides/deploy/automation-credentials) | | **Assessment** | A scorer's verdict on one execution trace. [Evaluation overview](/guides/evaluate/overview) | | **Audit ledger** | The tenant-isolated, append-only, hash-chained, signed record of decisions and attribution. It holds references and hashes, never content. [Audit and evidence](/guides/audit/audit-and-evidence) | | **Canary** | A version a tenant serves at a percentage share alongside its baseline. [Roll out and roll back](/guides/deploy/rollout-and-rollback) | | **Ceiling** | A declared or administered upper bound that only ever narrows: models, external hosts, connector tools, agent authority. [Policy, approvals, and evidence](/concepts/governance) | | **Connection** | A configured workspace service connection; personal integrations additionally require each user's own account. [MCP connectors](/guides/connect-tools/mcp-connectors) | | **Connector** | A governed MCP endpoint an administrator defines, with a discovered tool inventory. [MCP connectors](/guides/connect-tools/mcp-connectors) | | **Context** | Local CLI configuration for one Tanomis environment and its login. [CLI configuration](/reference/cli-configuration) | | **Delegation** | One agent handing a skill invocation to another with narrowed authority. [Delegate work to another agent](/guides/run/delegate-to-another-agent) | | **Enablement** | The tenant-scoped assignment that lets a version receive traffic. [Agents and versions](/concepts/agents-and-versions) | | **Execution trace** | One OpenTelemetry trace per run: model calls, tool calls, timing, and captured content. [Capture execution traces](/guides/evaluate/capture-traces) | | **Experiment** | A candidate version replayed over a dataset version, with aggregate metrics. [Evaluation overview](/guides/evaluate/overview) | | **Global agent** | An agent registered once at platform level and enabled per tenant, each tenant on the version you choose. [Platform model](/concepts/platform-model) | | **Golden snapshot** | A warm-process checkpoint of an agent image over an empty workspace, restored to start sessions in under a second. [Agents and versions](/concepts/agents-and-versions) | | **Harness** | The framework an agent is written against: Claude Agent SDK or Deep Agents. [Choose a harness](/guides/build/choose-a-harness) | | **Idempotency key** | A caller-chosen key that makes a retry return the original session or run. [Sessions and runs](/concepts/sessions-and-runs) | | **Interaction** | A durable question or permission request a run pauses on until a person answers. [Approval-required actions](/guides/run/approval-required-actions) | | **Invoking principal** | The person or service that started a request, resolved from the access token. [Identity and access](/concepts/identity-and-access) | | **LLM gateway** | The per-tenant gateway every model call passes through: authorization, metering, tracing. [Choose models](/guides/build/choose-models) | | **Manifest** | `agent.yaml`, the version's declared contract. [Agent manifest](/reference/agent-manifest) | | **Organization** | The customer account: members, roles, invitations, tenant attachments. [Organizations and tenants](/concepts/organizations-and-tenants) | | **Platform operator** | Whoever runs the Tanomis deployment: Tanomis staff in the hosted service, your own team when self-hosted. [Platform model](/concepts/platform-model) | | **Run** | One admitted unit of work inside a session; its resource is the authoritative status. [Sessions and runs](/concepts/sessions-and-runs) | | **Scope ceiling** | The most authority an agent's own tokens can ever carry. [Set the authorization ceiling](/guides/govern/set-the-authorization-ceiling) | | **Sandbox tenant** | A tenant that serves no customer, where developers and CI test before a rollout reaches customers. [Environments and contexts](/guides/deploy/environments-and-contexts) | | **Session** | The continuity boundary for one enabled agent version: workspace, runs, files, interactions. [Sessions and runs](/concepts/sessions-and-runs) | | **Skill** | A unit of work an agent accepts from another agent. [Delegate work to another agent](/guides/run/delegate-to-another-agent) | | **Tenant** | One of your customers: an isolated runtime with its own policy, connectors, and data. [Organizations and tenants](/concepts/organizations-and-tenants) | | **Version** | One immutable build of an agent with its contract captured at registration. [Versions and statuses](/guides/deploy/versions-and-statuses) | | **Warm start** | Starting a session from a golden snapshot instead of booting the harness cold. [Agents and versions](/concepts/agents-and-versions) | | **Workspace** | Your view of one organization and one tenant, with your role and capabilities. [Organizations and tenants](/concepts/organizations-and-tenants) | | **Integration** | An agent manifest declaration naming a configured connector and the tools that version may use. The connector supplies the endpoint and account mode. [MCP connectors](/guides/connect-tools/mcp-connectors) | --- Source: /reference/limits # Limits and quotas Request, session, file, and rate limits, with the status you see when you hit one. Every limit an application can hit, in one table. The numbers are being confirmed for publication. This page is planned and not yet written, because the capability it describes is not yet generally available. The structure is here so you can see where it fits; the notice comes off when it ships. ## What this page will cover - Idempotency key and payload sizes. - Session lifetime and idle limits. - File size and count limits. - Rate limits and the 429 contract. ## Meanwhile - The [Platform API overview](/api#errors-and-retries) already describes the retry contract for each status. --- Source: /reference/organization-management-api # Organization management API Human owner/admin HTTP endpoints for machine client credentials and tenant lifecycle. These endpoints manage your organization's automation clients and tenants. Use a **human owner or admin** bearer token and the public platform API origin. An automation client cannot call them, even to rotate its own credentials. The organization ID comes from the path; no workspace selection is needed for these organization-scoped operations. For interactive administration, the [Console and M2M CLI](/guides/deploy/automation-credentials) and [tenant commands](/guides/manage/tenants-and-environments) provide the same operations. ## Machine clients All paths below are relative to `/v1/organizations/{organization_id}/m2m`. `{client_id}` is the platform record ID (`client.id`), not the OAuth `client_id` returned for credential use. `{credential_id}` is a credential metadata ID. | Method | Path | Result | |---|---|---| | GET | base path | List `{clients: [...]}` without secrets. | | GET | `/profiles` | Supported permission profiles, scopes, and catalog version. | | POST | base path | Create a client and issue its first credential. | | GET | `/{client_id}` | Inspect `{client, credentials}` without secrets. | | PATCH | `/{client_id}` | Rename, change permissions, disable, or enable. | | POST | `/{client_id}/credentials` | Add a credential for rotation. | | DELETE | `/{client_id}/credentials/{credential_id}` | Revoke one credential. | | DELETE | `/{client_id}` | Delete the client and withdraw access. | ### Create Send `Content-Type: application/json` and an `Idempotency-Key` of 8–256 characters: ```json { "name": "production-deployer", "profile": "deploy", "expires_in_days": 90 } ``` `description` is optional. Use `observe`, `invoke`, or `deploy`, or set `profile` to `custom` with an array of supported `scopes` instead of a named profile. The default credential lifetime is 90 days and maximum 365. A successful issue returns HTTP 201 with `{client, credential, client_id, client_secret}`. The secret is returned once and cannot be read later. Retrying with the same idempotency key cannot issue another secret; an already completed issue returns a conflict without disclosing it again. Do not log or cache the response. Inspect metadata and rotate if a response was lost. ### Update Fetch the client and send its current `version` with a PATCH: ```json {"version": 3, "name": "release-deployer", "profile": "deploy"} ``` To disable, send `{"version": 3, "status": "disabled"}` instead. Use `active` to re-enable. Do not combine a status transition with name or permission changes. A stale version conflicts; fetch the record again before deciding whether to retry. ### Rotate and revoke Credential creation also requires an `Idempotency-Key` and a non-empty label: ```json {"label": "September rotation", "expires_in_days": 90} ``` At most two live, unexpired credentials are allowed per client. Switch consumers to the new secret, then DELETE the old credential. At most 20 non-deleted clients are allowed per organization. Updates can return 200 or 202; revocation and deletion can return 204 or 202. A 202 means cleanup or application of a change is still pending. Inspect the resource for progress. Client disable/delete and scope removal apply at the next platform authorization decision without waiting for token expiry. Revoking an individual credential prevents new token issuance once provider cleanup completes; already-issued tokens can remain valid until expiry. Disable the client if immediate withdrawal is required. Machine management responses use `Cache-Control: no-store`. Mutations are rate-limited per organization and human actor; handle HTTP 429 with backoff. A conflict can indicate a changed version, lifecycle state, credential limit, or a completed one-time issue; inspect before retrying. ## Tenants Paths are relative to `/v1/organizations/{organization_id}/tenants`. | Method | Path | Operation | |---|---|---| | GET | base path | List organization tenants. | | POST | base path | Create a tenant. | | GET | `/{tenant_id}` | Inspect lifecycle and attachment state. | | POST | `/{tenant_id}:retry` | Retry provisioning. | | POST | `/{tenant_id}:offboard` | Withdraw access and start irreversible cleanup. | Create with an `Idempotency-Key` and JSON body: ```json {"display_name": "bluewater-bank", "mode": "saas"} ``` The deployment controls supported modes and quotas. Creation returns 202 while provisioning runs. A tenant becomes usable only when `status` is `active` and attachment `state` is `ready`. Organization machine clients gain reach automatically at that point. Offboard with `{"confirm": ""}`. It returns 202, withdraws machine reach immediately, and cleans up asynchronously. Preserve required data before confirming. For runtime calls made **by** a machine client, see [Authenticate a backend](/guides/run/authenticate-your-backend) and the [application API reference](/api).