# agent

Registered Amfelt agents and their approval lifecycle.

**Amfelt REST API** · v1.13.0 · `https://api.amfelt.com`

---
### GET `/v1/agents` {#listagents}

**List agents.**

Returns a page of agents registered to the caller's organization, optionally filtered by status.

#### Parameters

| Name | In  | Required | Type | Description |
| ---- | --- | -------- | ---- | ----------- |
| `X-Tenant-Slug` | header | Yes | string | Slug of the organization to scope the request to; required for user-authenticated requests. |
| `status` | query | No | string[] | Filter agents by status; repeat the parameter to pass multiple values. |
| `cursor` | query | No | string | Opaque cursor for the next page; pass back the 'next_cursor' from a prior response. |
| `limit` | query | No | integer | Maximum number of items to return per page. |

#### Responses

**200** — A page of agents.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `data` | [Agent](#agent)[] | Yes | The page of agents. |
| `next_cursor` | [string null] | No | Opaque cursor for the next page, or null when this is the final page. Examples: `eyJpZCI6OTk5fQ`. |


**400** — The request was malformed or failed validation.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**401** — The request lacks valid credentials.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


#### Security

bearer_jwt

---

### POST `/v1/agents/{agent_id}/upgrade` {#upgradeagent}

**Upgrade an agent to a published release, the newest by default.**

Asks the agent to replace its own binary with a published, non-prerelease release and restart into it. The optional body names the target `version`. When the body or its `version` is absent, the target is the newest release, which is the same value version skew is derived against.
The agent's effective target is the version its environments' policies derive, the strictest of them winning (the agent's `target_version`).
A `version` lower than the running version moves the agent down, which this operation does in one case only: the agent's `target_state` is `rollback_pending` and `version` equals its effective target. That is the manual retry of the downgrade the orchestrator applies once the agent is idle, and it needs no permission beyond `agents.upgrade`. Any other lower `version` is refused with `downgrade_requires_rollback`: an agent moves down only after a rollback of its environment's pin (changeAgentVersionPolicy with `rollback: true`).
A manual upgrade to the effective target, the rollback retry included, is also the retry of a failed upgrade: it clears the pause that failure set (the agent's `upgrade_paused`). A manual upgrade to any other version leaves the pause in place and also needs the `agents.versions.manage` permission. The pause itself is never a reason to refuse a manual upgrade; every other refusal still applies.
Only agents reporting the `native` deployment mode are eligible. Every other mode — including `unknown` — is owned by an orchestrator or a package manager that would overwrite the change, so the request is refused rather than attempted.

#### Parameters

| Name | In  | Required | Type | Description |
| ---- | --- | -------- | ---- | ----------- |
| `X-Tenant-Slug` | header | Yes | string | Slug of the organization to scope the request to; required for user-authenticated requests. |
| `agent_id` | path | Yes | string | Identifier of the agent. |

#### Request body

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `version` | string | No | The published, non-prerelease release to install. A version below the running one is refused with `downgrade_requires_rollback`, unless it is the agent's effective target and the agent's `target_state` is `rollback_pending`. Examples: `5.1.3`. |


#### Responses

**202** — The upgrade was dispatched to the agent. The body carries the correlation id every outcome for this dispatch is keyed by — the operator-visible result arrives later on the agent's `last_command`, not in this response. A `queued_offline` status means the agent was not connected and the frame was persisted, so that result may be minutes rather than seconds away.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `agent_id` | string | Yes |  |
| `id` | string | Yes | Correlation id — matches the event frames this command produces. |
| `status` | string | Yes | `queued` means the frame went straight down a live connection. `queued_offline` means the agent was not connected and the frame was persisted for delivery when it reconnects — so a correlated outcome may be minutes away rather than seconds. Valid values are: `queued`, `queued_offline`. |
| `type` | string | Yes |  Examples: `cmd.upgrade`. |


**400** — The request was malformed or failed validation.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**401** — The request lacks valid credentials.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**403** — The caller is authenticated but not permitted to perform the request.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**404** — The specified resource was not found.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**409** — The agent cannot be upgraded in its current state.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `message` | string | Yes | Human-readable explanation, safe to show an operator. Examples: `This agent runs in a container; upgrade its image instead.`. |
| `reason` | string | Yes | Machine-readable refusal reason. `upgrade_externally_managed` — the agent's platform owns its binary and would overwrite the change; `message` names what to update instead. `upgrade_agent_unreachable` — the agent is not currently connected, so the command could not be delivered. Retry when it reconnects. `upgrade_agent_busy` — the agent is connected but a run is in flight. Retry when it is idle. `upgrade_not_needed` — there is nothing to upgrade to, either because the agent already runs the target release or because the newest release could not be determined. `approval_required` — the target is above the version a pinned environment of the agent holds. That pin must move first, through changeAgentVersionPolicy: a change to Production applies once its approval request is approved, and a change to any other environment applies at once. `downgrade_requires_rollback` — the target is below the running version and is not a downgrade the agent is due. Only the effective target of an agent whose `target_state` is `rollback_pending` moves it down: an agent moves down only after a rollback of its environment's pin. Valid values are: `upgrade_externally_managed`, `upgrade_agent_unreachable`, `upgrade_agent_busy`, `upgrade_not_needed`, `approval_required`, `downgrade_requires_rollback`. Examples: `upgrade_externally_managed`. |


**422** — The named `version` is not a published, non-prerelease release.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


#### Security

bearer_jwt

---

### GET `/v1/agents/{agent_id}/status` {#getagentstatus}

**Get agent status.**

Returns the current status and last-seen time of an agent. Public: a pending agent polls this before it holds any credentials.

#### Parameters

| Name | In  | Required | Type | Description |
| ---- | --- | -------- | ---- | ----------- |
| `agent_id` | path | Yes | string | Identifier of the agent. |

#### Responses

**200** — The agent status.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `agent_id` | string | Yes | Identifier of the agent. |
| `last_seen_at` | string | No | Timestamp the agent was last seen; absent if it has never connected. |
| `status` | [AgentLifecycleStatus](#agent-lifecycle-status) | Yes |  |


**400** — The request was malformed or failed validation.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**404** — The specified resource was not found.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


---

### POST `/v1/agents/{agent_id}/reject` {#rejectagent}

**Reject an agent.**

Transitions a pending agent to the revoked state. A reason is required and recorded in the audit trail.

#### Parameters

| Name | In  | Required | Type | Description |
| ---- | --- | -------- | ---- | ----------- |
| `X-Tenant-Slug` | header | Yes | string | Slug of the organization to scope the request to; required for user-authenticated requests. |
| `agent_id` | path | Yes | string | Identifier of the agent. |

#### Request body _(required)_

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `reason` | string | Yes | Reason for the rejection; recorded in the audit trail. |


#### Responses

**200** — The agent was rejected.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `agent_id` | string | Yes | Identifier of the agent. |
| `status` | [AgentLifecycleStatus](#agent-lifecycle-status) | Yes |  |


**400** — The request was malformed or failed validation.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**401** — The request lacks valid credentials.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**403** — The caller is authenticated but not permitted to perform the request.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**404** — The specified resource was not found.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**409** — The request conflicts with the current state of the resource.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


#### Security

bearer_jwt

---

### GET `/v1/agents/{agent_id}/icpc-events` {#listagenticpcevents}

**List ICPC events observed for an agent.**

Returns the frames the agent sent that matched no registered handler, newest first. This is where a command's outcome lands: refusals, smoke-test failures and restart failures all arrive as `error` frames and are readable only here. A caller who does not hold both `projects.update` and `sources.write` reads some answer frames without their payload, marked `payload_withheld`; `IcpcEvent.payload` says which.

#### Parameters

| Name | In  | Required | Type | Description |
| ---- | --- | -------- | ---- | ----------- |
| `X-Tenant-Slug` | header | Yes | string | Slug of the organization to scope the request to; required for user-authenticated requests. |
| `agent_id` | path | Yes | string | Identifier of the agent. |
| `limit` | query | No | integer | Maximum number of events to return. |
| `type` | query | No | string | Return only events of this exact frame type. |

#### Responses

**200** — The agent's observed ICPC events.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `agent_id` | string | Yes |  |
| `count` | integer | Yes | Number of events in this response, after the limit is applied. |
| `events` | [IcpcEvent](#icpc-event)[] | Yes |  |
| `total` | integer | Yes | Total events stored for this agent, ignoring the limit. |


**401** — The request lacks valid credentials.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**403** — The caller is authenticated but not permitted to perform the request.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**404** — The specified resource was not found.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


#### Security

bearer_jwt

---

### POST `/v1/agents/{agent_id}/icpc-cmd` {#postagenticpccommand}

**Dispatch an ICPC command to a connected agent.**

Queues a command frame on the agent's live ICPC connection. The 202 means the frame was queued for delivery, not that the agent accepted or completed it: everything the agent decides afterwards travels back as an ICPC event, readable via `/icpc-events`.
The `type` must name a `cmd.*` frame. The payload is opaque to the api — the agent-side handler for that command type interprets it — so this endpoint reaches any command the agent implements, including ones a dedicated endpoint would refuse. The agent-side gate is what constrains that, not this route.

#### Parameters

| Name | In  | Required | Type | Description |
| ---- | --- | -------- | ---- | ----------- |
| `X-Tenant-Slug` | header | Yes | string | Slug of the organization to scope the request to; required for user-authenticated requests. |
| `agent_id` | path | Yes | string | Identifier of the agent. |

#### Request body _(required)_

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `id` | string | No | Correlation id for the command. The server assigns a UUID when omitted, and echoes it back so the caller can tie an event to this dispatch. |
| `payload` | object | No | Opaque command arguments. The api does not interpret this — the agent-side handler for `type` defines its shape. |
| `type` | string | Yes | The command frame type. Must be a `cmd.*` name the agent implements; anything else is rejected before dispatch. Examples: `cmd.upgrade`. |


#### Responses

**202** — The command frame was queued for delivery to the agent.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `agent_id` | string | Yes |  |
| `id` | string | Yes | Correlation id — matches the event frames this command produces. |
| `status` | string | Yes | `queued` means the frame went straight down a live connection. `queued_offline` means the agent was not connected and the frame was persisted for delivery when it reconnects — so a correlated outcome may be minutes away rather than seconds. Valid values are: `queued`, `queued_offline`. |
| `type` | string | Yes |  Examples: `cmd.upgrade`. |


**400** — The request was malformed or failed validation.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**401** — The request lacks valid credentials.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**403** — The caller is authenticated but not permitted to perform the request.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**404** — No agent with that id, or no active ICPC connection for it.

**503** — The connection's send buffer is full — the agent is connected but not draining frames.

#### Security

bearer_jwt

---

### POST `/v1/agents/{agent_id}/approve` {#approveagent}

**Approve an agent.**

Transitions a pending agent to the active state. The approver must differ from the user that created the agent's invite.

#### Parameters

| Name | In  | Required | Type | Description |
| ---- | --- | -------- | ---- | ----------- |
| `X-Tenant-Slug` | header | Yes | string | Slug of the organization to scope the request to; required for user-authenticated requests. |
| `agent_id` | path | Yes | string | Identifier of the agent. |

#### Request body

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `reason` | string | No | Optional reason recorded in the audit trail. |


#### Responses

**200** — The agent was approved.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `agent_id` | string | Yes | Identifier of the agent. |
| `status` | [AgentLifecycleStatus](#agent-lifecycle-status) | Yes |  |


**401** — The request lacks valid credentials.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**403** — The caller is authenticated but not permitted to perform the request.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**404** — The specified resource was not found.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**409** — The request conflicts with the current state of the resource.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


#### Security

bearer_jwt

---

### GET `/v1/agents/{agent_id}` {#getagent}

**Get an agent.**

Returns a single agent by identifier, including its bound public key.

#### Parameters

| Name | In  | Required | Type | Description |
| ---- | --- | -------- | ---- | ----------- |
| `X-Tenant-Slug` | header | Yes | string | Slug of the organization to scope the request to; required for user-authenticated requests. |
| `agent_id` | path | Yes | string | Identifier of the agent. |

#### Responses

**200** — The agent.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `created_at` | string | Yes | Timestamp the agent was registered. |
| `deploy_mode` | string | No | How the agent was deployed, as it reported on its heartbeat. Absent when the agent has never reported one. 'unknown' means the agent reported but could not classify its own host. Valid values are: `native`, `docker`, `k8s`, `package_manager`, `unknown`. Examples: `docker`. |
| `display_name` | string | Yes | Human-readable name of the agent. |
| `externally_managed` | boolean | No | True when the agent does not report the `native` deployment mode, so it never replaces its own binary: whatever manages its image, chart or package moves it to `target_version`. Its `target_state` is derived like any other agent's. |
| `floor_version` | string | No | The version floor `upgrade_required_reason` refers to. Present only with `upgrade_required_reason`. Examples: `5.1.0`. |
| `id` | string | Yes | Unique identifier of the agent. |
| `key_fingerprint` | string | No | Fingerprint of the agent's bound public key. |
| `last_command` | [AgentCommandOutcome](#agent-command-outcome) | No |  |
| `last_seen_at` | string | No | Timestamp the agent was last seen; absent if it has never connected. |
| `liveness` | string | Yes | Liveness derived from the agent's last-seen time. Valid values are: `online`, `offline`, `never_seen`. Examples: `online`. |
| `policy_sources` | [EnvironmentSlug](#environment-slug)[] | No | The DTAP environments whose policies set `target_version`. Derived per request, never stored. Examples: `["production"]`. |
| `public_key_pem` | string | No | PEM-encoded Ed25519 public key bound to the agent; returned by the detail endpoint only. |
| `status` | [AgentLifecycleStatus](#agent-lifecycle-status) | Yes |  |
| `target_state` | string | No | How the agent's running `version` stands against `target_version`. Derived per request, never stored. `current` — the agent runs its target. `behind` — the agent runs below its target. `above_pin` — the agent runs above its target, and no rollback lowered that target, for example a fresh install of the newest release that joins a pinned environment. It is left there: nothing downgrades it automatically. `rollback_pending` — the agent runs above its target because a rollback lowered that target (changeAgentVersionPolicy with `rollback: true`), applied in Production once its approval request is approved and in any other environment as soon as an owner or admin makes it. The orchestrator downgrades the agent to its target once it is idle, audited as operation `rollback`; upgradeAgent to the target is the manual retry. `upgrade_required` — the running version or the target is below a version floor the api requires; `upgrade_required_reason` says which. The orchestrator moves an idle agent that reports the `native` deployment mode and is not `upgrade_paused` to `target_version` from three states only: `behind`, `rollback_pending`, and `upgrade_required` with `upgrade_required_reason` `agent_below_floor`, whose target is valid. It leaves an agent that is `current`, `above_pin`, or `upgrade_required` with `pin_below_floor`, whose pin must move first. An externally managed agent (`externally_managed`) is derived the same way, so it can be `behind` or `upgrade_required` too. Valid values are: `current`, `behind`, `above_pin`, `rollback_pending`, `upgrade_required`. Examples: `behind`. |
| `target_version` | string | No | The agent's effective target: the version its environments' policies derive, the strictest of them winning (the lowest pinned version, otherwise the newest release once its settle delay has passed). Derived per request, never stored. Absent when it cannot be derived. Examples: `5.1.3`. |
| `tenant_id` | string | Yes | Identifier of the owning organization. |
| `updated_at` | string | No | Timestamp the agent was last modified; returned by the detail endpoint only. |
| `upgrade_failure_reason` | string | No | The classified reason the agent's last upgrade failed. Present while `upgrade_paused` is true. It is a code the agent reported, for example `upgrade_smoke_test_failed` or `upgrade_wrong_version`, or `ack_timeout` when the agent never acknowledged the upgrade. Examples: `upgrade_smoke_test_failed`. |
| `upgrade_paused` | boolean | No | True when the agent's last upgrade failed, so automatic upgrades for it are paused and it keeps running its current binary. A manual upgradeAgent to its `target_version` is the retry: it clears the pause. A manual upgrade to any other version leaves it set. |
| `upgrade_required_reason` | string | No | Why the agent is `upgrade_required`. Present only when `target_state` is `upgrade_required`. `agent_below_floor` — the running version is below `floor_version`. `pin_below_floor` — `target_version` is a pin below `floor_version`, for example a floor raised after the pin was saved. The pin moves only by a promotion, never automatically. Valid values are: `agent_below_floor`, `pin_below_floor`. Examples: `pin_below_floor`. |
| `version` | string | No | Semantic version reported by the agent at handshake. |
| `version_skew` | string | No | Whether the agent's reported version is behind the newest published agent release. Derived per request, never stored. 'unknown' when the agent has never reported a version or the release list is unavailable. Valid values are: `up_to_date`, `behind`, `ahead`, `unknown`. Examples: `behind`. |


**401** — The request lacks valid credentials.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**403** — The caller is authenticated but not permitted to perform the request.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**404** — The specified resource was not found.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


#### Security

bearer_jwt

---

### DELETE `/v1/agents/{agent_id}` {#deregisteragent}

**Deregister an agent.**

Transitions the agent to the revoked (terminal) state. A second attempt on an already-revoked agent returns 409.

#### Parameters

| Name | In  | Required | Type | Description |
| ---- | --- | -------- | ---- | ----------- |
| `X-Tenant-Slug` | header | Yes | string | Slug of the organization to scope the request to; required for user-authenticated requests. |
| `agent_id` | path | Yes | string | Identifier of the agent. |

#### Responses

**200** — The agent was deregistered.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `agent_id` | string | Yes | Identifier of the agent. |
| `status` | [AgentLifecycleStatus](#agent-lifecycle-status) | Yes |  |


**401** — The request lacks valid credentials.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**403** — The caller is authenticated but not permitted to perform the request.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**404** — The specified resource was not found.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


**409** — The request conflicts with the current state of the resource.

`application/json`

| Property | Type | Required | Description |
|---|---|---|---|
| `code` | string | Yes | Stable, snake_case token identifying the error condition. See ErrorCode for the api's canonical named vocabulary — this field is deliberately not restricted to it, since handlers may emit other ad-hoc codes not listed there (e.g. not_implemented). Examples: `invalid_argument`. |
| `field` | string | No | Optional hint identifying the request field the error refers to, in snake_case (e.g. "slug", "display_name", "token"). Set by handlers that validate multiple fields and want to enable field-targeted UX in clients. Absent when the error does not map to a single field. Examples: `slug`. |
| `message` | string | Yes | Human-readable description of the error. Examples: `limit must be between 1 and 100`. |


#### Security

bearer_jwt

---

