> ## Documentation Index
> Fetch the complete documentation index at: https://noradocs.solomontsao.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Agents

# Agents API: deploy and manage agent runtimes

> Deploy, start, stop, restart, and monitor Nora agent containers. Covers all lifecycle endpoints including live stats and gateway URL resolution.

The Agents API is the core of Nora. You use it to deploy new agent containers, control their lifecycle (start, stop, restart, redeploy, delete), poll live resource stats, and retrieve the gateway UI URL for direct browser access. Read endpoints include agents you own directly and agents shared with you through workspaces. Destructive deletion remains limited to the direct agent owner.

<Warning>
  A workspace API key is confined to agents assigned to its exact workspace. The same issuing user
  may belong to other workspaces, but the key cannot list, read, mutate, proxy, export, or execute
  against those agents. Existing Remote Docker agent operations are session-only because they can
  reach host credentials and privileged cleanup paths.
</Warning>

Agent export and live-filesystem routes are session-only for every backend. Their allowed runtime
roots can contain provider credentials, gateway secrets, paired-device state, and conversation
sessions, so workspace API keys cannot call `/agents/:id/export` or `/agents/:id/files/*` even when
the agent is assigned to the key's workspace.

<Note>
  An agent moves through the following statuses: `queued` → `deploying` → `running`. From `running`
  it can transition to `warning`, `error`, or `stopped`. Use `POST /agents/:id/redeploy` to recover
  an agent that is in `warning`, `error`, or `stopped` state.
</Note>

***

## List agents

Returns all agents accessible to the authenticated user, ordered by creation time (newest first). Use `scope=owned` when a UI or integration needs only directly owned agents, such as assignment candidates or quota counters.

For workspace API keys, both `accessible` and `owned` are evaluated inside the key's exact bound
workspace, and the response does not include assignments from another workspace.

```
GET /agents
```

### Query parameters

<ParamField query="scope" type="string">
  Optional. `accessible` (default) includes directly owned and workspace-shared agents. `owned`
  returns only directly owned agents.
</ParamField>

<CodeGroup>
  ```bash curl theme={null} theme={null}
  curl http://localhost:8080/api/agents \
    -H "Authorization: Bearer $TOKEN"
  ```
</CodeGroup>

```json theme={null} theme={null}
[
  {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "My Research Agent",
    "status": "running",
    "sandbox_type": "standard",
    "vcpu": 2,
    "ram_mb": 2048,
    "disk_gb": 20,
    "node": "worker-01",
    "container_id": "abc123def456",
    "created_at": "2025-03-10T09:00:00.000Z",
    "isDirectOwner": true,
    "effectiveRole": "owner",
    "workspaces": [
      {
        "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
        "name": "Production",
        "role": "owner"
      }
    ]
  }
]
```

***

## Deploy an agent

Create and queue a new agent for deployment. The agent record is created immediately with status `queued`; the actual container is started asynchronously by the deployment worker.

```
POST /agents/deploy
```

### Request body

<ParamField body="name" type="string">
  Human-readable agent name. Maximum 100 characters. Control characters are stripped. Defaults to `OpenClaw-Agent-<random>`.
</ParamField>

<ParamField body="runtime_family" type="string" default="server default">
  Runtime contract to deploy: `openclaw` or `hermes`. If omitted, Nora uses the control plane's
  configured default runtime family.
</ParamField>

<ParamField body="deploy_target" type="string" default="server default">
  Placement family: GA `docker` or `k8s`, or experimental `remote-docker` or `proxmox`. Registered
  targets still require their concrete `execution_target_id`; Nora does not choose a cluster or
  remote host automatically.
</ParamField>

<ParamField body="execution_target_id" type="string" default="derived from deploy_target">
  Concrete placement id. Use `docker` for local Docker, `k8s:<cluster-id>` for an Admin-registered
  Kubernetes cluster, `remote:<host-id>` for a registered Remote Docker host, or `proxmox` for the
  configured Proxmox target.
</ParamField>

<ParamField body="sandbox_profile" type="string" default="standard">
  `standard` or experimental `nemoclaw`. Hermes supports only `standard`. `nemoclaw` requires the
  profile to be enabled, and NemoClaw on Proxmox remains blocked. The legacy `sandbox` field is
  still accepted as an alias, but new clients should send `sandbox_profile`.
</ParamField>

<ParamField body="vcpu" type="number" default="operator deployment default (fallback 1)">
  Number of virtual CPUs. On self-hosted deployments, an explicit value is clamped to operator
  limits. PaaS ignores the request value and uses the platform-wide deployment default.
</ParamField>

<ParamField body="ram_mb" type="number" default="operator deployment default (fallback 1024)">
  RAM allocation in MB. PaaS ignores the request value and uses the platform-wide deployment
  default. Minimum `512` when a self-hosted caller supplies a value.
</ParamField>

<ParamField body="disk_gb" type="number" default="operator deployment default (fallback 10)">
  Requested disk size in GB. PaaS ignores the request value and uses the platform-wide deployment
  default. Minimum `1` when a self-hosted caller supplies a value. Kubernetes and Proxmox use this
  value for provisioned storage; Docker and Remote Docker store it as deployment metadata but do not
  enforce it as a container storage quota.
</ParamField>

<ParamField body="migration_draft_id" type="string">
  Optional migration draft UUID. Migration drafts can contain complete managed state, so this field
  is session-only and returns `403 session_required` when supplied with a workspace API key.
</ParamField>

<Warning>
  On PaaS deployments the operator-managed platform defaults determine `vcpu`, `ram_mb`, and
  `disk_gb`; plan tiers currently control deployment count, not per-agent resource size.
</Warning>

### Response

Returns the newly created agent record.

When a workspace API key creates a non-Remote agent, Nora creates the agent and its assignment to the
key's workspace in one database statement before publishing any deployment job. Adoption and
duplication follow the same workspace-binding rule.

<ResponseField name="id" type="string">
  UUID of the new agent.
</ResponseField>

<ResponseField name="name" type="string">
  Agent display name.
</ResponseField>

<ResponseField name="status" type="string">
  `queued` — deployment is pending.
</ResponseField>

<ResponseField name="sandbox_type" type="string">
  `standard` or `nemoclaw`.
</ResponseField>

<ResponseField name="runtime_family" type="string">
  Resolved runtime family, `openclaw` or `hermes`.
</ResponseField>

<ResponseField name="deploy_target" type="string">
  Resolved placement family, such as `docker`, `k8s`, or `remote-docker`.
</ResponseField>

<ResponseField name="execution_target_id" type="string">
  Exact selected target, such as `docker`, `k8s:aks-eastus2`, or `remote:build-host`.
</ResponseField>

<ResponseField name="sandbox_profile" type="string">
  Resolved sandbox profile, `standard` or `nemoclaw`.
</ResponseField>

<ResponseField name="vcpu" type="number">
  Allocated vCPU count.
</ResponseField>

<ResponseField name="ram_mb" type="number">
  Allocated RAM in MB.
</ResponseField>

<ResponseField name="disk_gb" type="number">
  Resolved disk request in GB. For Docker and Remote Docker this is metadata, not an enforced
  storage quota.
</ResponseField>

<ResponseField name="node" type="string">
  Scheduler node assigned to this agent.
</ResponseField>

<ResponseField name="container_name" type="string">
  Generated runtime resource name, such as a Docker container name or Kubernetes Deployment name.
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 creation timestamp.
</ResponseField>

<CodeGroup>
  ```bash curl theme={null} theme={null}
  curl -X POST http://localhost:8080/api/agents/deploy \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "name":"My Agent",
      "runtime_family":"openclaw",
      "deploy_target":"docker",
      "execution_target_id":"docker",
      "sandbox_profile":"standard",
      "vcpu":2,
      "ram_mb":2048,
      "disk_gb":20
    }'
  ```
</CodeGroup>

```json theme={null} theme={null}
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "name": "My Agent",
  "status": "queued",
  "runtime_family": "openclaw",
  "deploy_target": "docker",
  "execution_target_id": "docker",
  "sandbox_profile": "standard",
  "sandbox_type": "standard",
  "vcpu": 2,
  "ram_mb": 2048,
  "disk_gb": 20,
  "node": "worker-01",
  "container_name": "nora-oclaw-my-agent-1h8z2k",
  "created_at": "2025-03-10T09:00:00.000Z"
}
```

### Remote Docker and Hermes

Remote-host administration is intentionally session-only. Register and manually test a personal
host in **App -> Remote Hosts**, or have a platform admin create a platform host in **Admin -> Remote
Hosts**, before using its `remote:<id>` target. Workspace API keys cannot call personal
`/remote-hosts` or platform `/admin/remote-hosts` management endpoints, and every response masks
stored SSH private keys, passwords, and passphrases.

The dashboard currently lists registered Remote Docker hosts for OpenClaw only. Hermes Remote Docker
is an experimental advanced API path. After registration and testing, deploy it with a session JWT:

<CodeGroup>
  ```bash curl theme={null} theme={null}
  curl -X POST http://localhost:8080/api/agents/deploy \
    -H "Authorization: Bearer $SESSION_JWT" \
    -H "Content-Type: application/json" \
    -d '{
      "name":"Hermes Remote",
      "runtime_family":"hermes",
      "deploy_target":"remote-docker",
      "execution_target_id":"remote:build-host",
      "sandbox_profile":"standard",
      "vcpu":2,
      "ram_mb":4096,
      "disk_gb":20
    }'
  ```
</CodeGroup>

For a personal host, the session user must own it or have an `editor`, `admin`, or `owner` role in a
workspace where it is shared. For a platform host, a platform admin, the all-accounts setting, a
direct-user grant, a matching user-group grant, or an `editor+` workspace grant permits deployment.
A workspace viewer can see a granted host but cannot deploy to it. The created agent belongs to the
deploying user and is not automatically assigned to any workspace that supplied host access.

Remote Docker placement and every existing-agent Remote Docker operation are session-only. A
workspace API key receives `403 session_required` before host lookup, persistence, queue publication,
gateway/file access, or lifecycle cleanup when the source, destination, or persisted agent uses
Remote Docker. API-key deploys to non-Remote targets continue to use the normal `agents:write` scope
and are automatically assigned to the key's workspace. After a session Remote Docker deployment,
assign the agent explicitly if workspace access is required. Follow [Connect a Remote Docker
host](/guides/remote-docker) for setup, then use the [backend
reference](/configuration/provisioner-backends/remote-docker) for the full test, networking,
capacity, sharing, and revocation contract.

| Status | Condition                                                                                          |
| ------ | -------------------------------------------------------------------------------------------------- |
| `400`  | Invalid or unavailable runtime selection, unknown/inaccessible concrete target, or name too long   |
| `402`  | Billing limit reached                                                                              |
| `403`  | API key lacks `agents:write`, uses a migration draft, or targets a session-only Remote Docker path |

***

## Adopt an external runtime

Register an already-running OpenClaw or Hermes endpoint without provisioning new compute. Nora
stores the gateway credential encrypted, monitors and proxies the runtime, and marks the agent as
`external`. Lifecycle actions are unavailable because Nora does not own the process; deleting the
agent only deregisters it.

```
POST /agents/adopt
```

Provide `runtime_family`, either `url` or `host` plus optional `port`, and a cryptographically
generated `gateway_token`. The token must contain at least 32 characters, have no whitespace, and
be generated from a cryptographically secure random source. Hosted PaaS accepts only public
endpoints; self-hosted operators may adopt a private-network endpoint that passes the gateway proxy's
host and port safety checks. Nora rechecks this contract when an adopted runtime connects, so legacy
rows with weak credential shapes fail closed until the external runtime token is rotated.

```bash theme={null} theme={null}
GATEWAY_TOKEN="$(openssl rand -hex 32)"
curl -X POST http://localhost:8080/api/agents/adopt \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"name\":\"Existing OpenClaw\",\"runtime_family\":\"openclaw\",\"url\":\"https://agent.example.com:18789\",\"gateway_token\":\"$GATEWAY_TOKEN\"}"
```

The provided token must already be configured on the external runtime; generating a replacement in
the example is appropriate only when you can update that runtime to use the same value. Workspace
API keys require `agents:write`, and successful adoptions are atomically assigned to the key's bound
workspace.

| Status | Condition                                                            |
| ------ | -------------------------------------------------------------------- |
| `400`  | Weak token, invalid runtime family, unsafe endpoint, or invalid port |
| `402`  | Billing limit reached                                                |
| `403`  | Missing `agents:write` scope                                         |

***

## Get agent

Fetch a single agent by ID. The status is reconciled against the live container state on every call — if the container has stopped unexpectedly the stored status is updated before the response is returned.

```
GET /agents/:id
```

### Path parameters

<ParamField path="id" type="string" required>
  Agent UUID.
</ParamField>

<CodeGroup>
  ```bash curl theme={null} theme={null}
  curl http://localhost:8080/api/agents/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
    -H "Authorization: Bearer $TOKEN"
  ```
</CodeGroup>

```json theme={null} theme={null}
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "name": "My Agent",
  "status": "running",
  "container_id": "abc123def456",
  "sandbox_type": "standard",
  "vcpu": 2,
  "ram_mb": 2048,
  "disk_gb": 20,
  "node": "worker-01",
  "gateway_host_port": 32101,
  "created_at": "2025-03-10T09:00:00.000Z"
}
```

***

## Start agent

Start a stopped agent's existing container.

```
POST /agents/:id/start
```

### Path parameters

<ParamField path="id" type="string" required>
  Agent UUID.
</ParamField>

Returns the updated agent record with `status: "running"`.

<CodeGroup>
  ```bash curl theme={null} theme={null}
  curl -X POST http://localhost:8080/api/agents/a1b2c3d4-e5f6-7890-abcd-ef1234567890/start \
    -H "Authorization: Bearer $TOKEN"
  ```
</CodeGroup>

| Status | Condition                               |
| ------ | --------------------------------------- |
| `400`  | Agent has no container — redeploy first |
| `404`  | Agent not found                         |

***

## Stop agent

Gracefully stop a running agent's container. The status is set to `stopped` regardless of whether the container was already stopped.

```
POST /agents/:id/stop
```

### Path parameters

<ParamField path="id" type="string" required>
  Agent UUID.
</ParamField>

Returns the updated agent record with `status: "stopped"`.

<CodeGroup>
  ```bash curl theme={null} theme={null}
  curl -X POST http://localhost:8080/api/agents/a1b2c3d4-e5f6-7890-abcd-ef1234567890/stop \
    -H "Authorization: Bearer $TOKEN"
  ```
</CodeGroup>

***

## Restart agent

Restart a running agent's container in place (does not redeploy).

```
POST /agents/:id/restart
```

### Path parameters

<ParamField path="id" type="string" required>
  Agent UUID.
</ParamField>

### Response

<ResponseField name="success" type="boolean">
  `true` when the container was restarted.
</ResponseField>

<CodeGroup>
  ```bash curl theme={null} theme={null}
  curl -X POST http://localhost:8080/api/agents/a1b2c3d4-e5f6-7890-abcd-ef1234567890/restart \
    -H "Authorization: Bearer $TOKEN"
  ```
</CodeGroup>

```json theme={null} theme={null}
{ "success": true }
```

| Status | Condition                               |
| ------ | --------------------------------------- |
| `400`  | Agent has no container — redeploy first |

***

## Redeploy agent

Re-queue an agent for a full redeploy. Only available when the agent is in `warning`, `error`, or `stopped` state. Clears the existing container reference and submits a new deployment job. For Kubernetes-backed agents, the job deletes the previous Deployment, Service, and bootstrap ConfigMap before creating the replacement.

```
POST /agents/:id/redeploy
```

### Path parameters

<ParamField path="id" type="string" required>
  Agent UUID.
</ParamField>

### Response

<ResponseField name="success" type="boolean">
  `true` when the redeploy job was queued.
</ResponseField>

<ResponseField name="status" type="string">
  `queued`.
</ResponseField>

<CodeGroup>
  ```bash curl theme={null} theme={null}
  curl -X POST http://localhost:8080/api/agents/a1b2c3d4-e5f6-7890-abcd-ef1234567890/redeploy \
    -H "Authorization: Bearer $TOKEN"
  ```
</CodeGroup>

```json theme={null} theme={null}
{ "success": true, "status": "queued" }
```

| Status | Condition                                              |
| ------ | ------------------------------------------------------ |
| `400`  | Agent is not in `warning`, `error`, or `stopped` state |

***

## Delete agent

Permanently delete an agent and destroy its container.

```
DELETE /agents/:id
```

### Path parameters

<ParamField path="id" type="string" required>
  Agent UUID.
</ParamField>

### Response

<ResponseField name="success" type="boolean">
  `true` when the agent was deleted.
</ResponseField>

<CodeGroup>
  ```bash curl theme={null} theme={null}
  curl -X DELETE http://localhost:8080/api/agents/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
    -H "Authorization: Bearer $TOKEN"
  ```
</CodeGroup>

```json theme={null} theme={null}
{ "success": true }
```

<Warning>
  Deletion is irreversible. All agent data, channel configuration, and integration connections are
  permanently removed.
</Warning>

***

## Get live stats

Return a single real-time resource snapshot from the running container. Polls the Docker daemon directly.

```
GET /agents/:id/stats
```

### Path parameters

<ParamField path="id" type="string" required>
  Agent UUID.
</ParamField>

### Response

<ResponseField name="cpu_percent" type="number">
  CPU utilisation as a percentage across all cores.
</ResponseField>

<ResponseField name="memory_usage_mb" type="number">
  Actual memory usage in MB (excludes page cache).
</ResponseField>

<ResponseField name="memory_limit_mb" type="number">
  Memory limit allocated to the container in MB.
</ResponseField>

<ResponseField name="memory_percent" type="number">
  Memory usage as a percentage of the limit.
</ResponseField>

<ResponseField name="network_rx_mb" type="number">
  Total received bytes across all network interfaces, in MB.
</ResponseField>

<ResponseField name="network_tx_mb" type="number">
  Total transmitted bytes across all network interfaces, in MB.
</ResponseField>

<ResponseField name="disk_read_mb" type="number">
  Total block device read bytes in MB.
</ResponseField>

<ResponseField name="disk_write_mb" type="number">
  Total block device write bytes in MB.
</ResponseField>

<ResponseField name="pids" type="number">
  Number of processes running inside the container.
</ResponseField>

<ResponseField name="uptime_seconds" type="number">
  Seconds since the container last started.
</ResponseField>

<ResponseField name="running" type="boolean">
  Whether the container is currently running.
</ResponseField>

<CodeGroup>
  ```bash curl theme={null} theme={null}
  curl http://localhost:8080/api/agents/a1b2c3d4-e5f6-7890-abcd-ef1234567890/stats \
    -H "Authorization: Bearer $TOKEN"
  ```
</CodeGroup>

```json theme={null} theme={null}
{
  "cpu_percent": 4.32,
  "memory_usage_mb": 312,
  "memory_limit_mb": 2048,
  "memory_percent": 15.23,
  "network_rx_mb": 1.04,
  "network_tx_mb": 0.52,
  "disk_read_mb": 0.1,
  "disk_write_mb": 0.04,
  "pids": 21,
  "uptime_seconds": 3600,
  "running": true
}
```

| Status | Condition                         |
| ------ | --------------------------------- |
| `409`  | Agent has no container            |
| `502`  | Could not reach the Docker daemon |

***

## Get stats history

Return time-series container stats stored by the background collector (sampled every 10 seconds, retained for 24 hours).

```
GET /agents/:id/stats/history
```

### Path parameters

<ParamField path="id" type="string" required>
  Agent UUID.
</ParamField>

### Query parameters

<ParamField query="range" type="string" default="15m">
  Preset time window. One of `5m`, `15m`, `30m`, `1h`, `6h`, `24h`. Ignored when `from` and `to` are
  provided.
</ParamField>

<ParamField query="from" type="string">
  ISO 8601 start timestamp for a custom range. Must be used together with `to`.
</ParamField>

<ParamField query="to" type="string">
  ISO 8601 end timestamp for a custom range. Must be used together with `from`.
</ParamField>

Returns an array of up to 2 000 stat records, ordered oldest-first.

<ResponseField name="cpu_percent" type="number">
  CPU % at sample time.
</ResponseField>

<ResponseField name="memory_usage_mb" type="number">
  Memory usage in MB at sample time.
</ResponseField>

<ResponseField name="memory_limit_mb" type="number">
  Memory limit in MB.
</ResponseField>

<ResponseField name="memory_percent" type="number">
  Memory % at sample time.
</ResponseField>

<ResponseField name="network_rx_mb" type="number">
  Cumulative received MB at sample time.
</ResponseField>

<ResponseField name="network_tx_mb" type="number">
  Cumulative transmitted MB at sample time.
</ResponseField>

<ResponseField name="disk_read_mb" type="number">
  Cumulative disk read MB at sample time.
</ResponseField>

<ResponseField name="disk_write_mb" type="number">
  Cumulative disk write MB at sample time.
</ResponseField>

<ResponseField name="pids" type="number">
  Process count at sample time.
</ResponseField>

<ResponseField name="recorded_at" type="string">
  ISO 8601 timestamp when the sample was recorded.
</ResponseField>

<CodeGroup>
  ```bash curl (preset range) theme={null} theme={null}
  curl "http://localhost:8080/api/agents/a1b2c3d4-e5f6-7890-abcd-ef1234567890/stats/history?range=1h" \
    -H "Authorization: Bearer $TOKEN"
  ```

  ```bash curl (custom range) theme={null} theme={null}
  curl "http://localhost:8080/api/agents/a1b2c3d4-e5f6-7890-abcd-ef1234567890/stats/history?from=2025-03-10T08:00:00Z&to=2025-03-10T09:00:00Z" \
    -H "Authorization: Bearer $TOKEN"
  ```
</CodeGroup>

```json theme={null} theme={null}
[
  {
    "cpu_percent": 3.14,
    "memory_usage_mb": 298,
    "memory_limit_mb": 2048,
    "memory_percent": 14.55,
    "network_rx_mb": 0.98,
    "network_tx_mb": 0.47,
    "disk_read_mb": 0.08,
    "disk_write_mb": 0.02,
    "pids": 19,
    "recorded_at": "2025-03-10T08:05:00.000Z"
  }
]
```

***

## Get gateway URL

Resolve the HTTP URL of the agent's embedded gateway control UI. The agent must be in `running` state.

```
GET /agents/:id/gateway-url
```

### Path parameters

<ParamField path="id" type="string" required>
  Agent UUID.
</ParamField>

### Response

<ResponseField name="url" type="string">
  Full HTTP URL to the gateway UI, e.g. `http://localhost:32101`.
</ResponseField>

<ResponseField name="port" type="number">
  Resolved host port number.
</ResponseField>

<CodeGroup>
  ```bash curl theme={null} theme={null}
  curl http://localhost:8080/api/agents/a1b2c3d4-e5f6-7890-abcd-ef1234567890/gateway-url \
    -H "Authorization: Bearer $TOKEN"
  ```
</CodeGroup>

```json theme={null} theme={null}
{
  "url": "http://localhost:32101",
  "port": 32101
}
```

| Status | Condition                                           |
| ------ | --------------------------------------------------- |
| `409`  | Agent is not running, or has no container           |
| `502`  | Could not inspect the container to resolve the port |

## Get agent budget

List an agent's LLM spend budgets with current spend attached. When spend crosses 100% of any cap, Nora pauses the runtime automatically (`paused_reason` becomes `budget_exceeded`); a warning event fires at the soft threshold.

```
GET /agents/:id/budget
```

### Response

<ResponseField name="budgets" type="array">
  One entry per period (`daily`, `weekly`, `monthly`) with `limitUsd`, `softThresholdPct`,
  `currentUsd`, `pct`, and `bucket` (`none` | `soft` | `hard`).
</ResponseField>

<ResponseField name="pausedReason" type="string | null">
  `budget_exceeded` while the agent is paused by a cap.
</ResponseField>

## Set agent budget

Create or update the budget for one period. Requires the `agents:write` scope.

```
PUT /agents/:id/budget
```

### Request body

<ParamField body="period" type="string" required>
  `daily`, `weekly`, or `monthly`.
</ParamField>

<ParamField body="limit_usd" type="number" required>
  Hard cap in USD. Crossing 100% pauses the runtime.
</ParamField>

<ParamField body="soft_threshold_pct" type="number">
  Warning threshold percent (default `80`). Emits `agent.budget_soft_exceeded`.
</ParamField>

## Delete agent budget

```
DELETE /agents/:id/budget/:budgetId
```

Removes the cap. A paused agent stays stopped until started; manually starting an agent clears `paused_reason`, and the budget sweep re-pauses it within a minute if a cap is still exceeded.
