> For the complete documentation index, see [llms.txt](https://docs.robotmoney.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.robotmoney.com/api-reference/agents-api.md).

# Agents API

Create tenant agents for the headless authorization path, register MCP agents, list the first-party agent catalog, route events to agents, run agents end-to-end, inspect runs, and halt agents.

| Operation                                             | Endpoint                                             |
| ----------------------------------------------------- | ---------------------------------------------------- |
| Create a tenant agent and one-time agent API key      | `POST /v1/agents`                                    |
| Register an external agent                            | `POST /v1/execution/agents/register`                 |
| List first-party agent catalog                        | `GET /v1/agents`                                     |
| Get an agent (definition; registration not yet wired) | `GET /v1/agents/{agent_id}`                          |
| List an agent's actions                               | `GET /v1/agents/{agent_id}/actions`                  |
| Route an event/intent                                 | `POST /v1/agents/route`                              |
| Run an agent end-to-end                               | `POST /v1/agents/run`                                |
| Enqueue an event for async routing                    | `POST /v1/agents/events`                             |
| Inspect a routing decision                            | `GET /v1/agents/routing-decisions/{id}`              |
| List runs                                             | `GET /v1/agents/runs`                                |
| Run detail                                            | `GET /v1/agents/runs/{run_id}`                       |
| Why a run did what it did                             | `GET /v1/agents/runs/{run_id}/why`                   |
| Evidence used for a run                               | `GET /v1/agents/runs/{run_id}/evidence`              |
| §6 gate trace for a run                               | `GET /v1/agents/runs/{run_id}/gate-trace`            |
| Canonical Proof for a run                             | `GET /v1/agents/runs/{run_id}/proof`                 |
| Halt one agent                                        | `POST /v1/agents/{agent_id}/halt`                    |
| Halt every agent in a category                        | `POST /v1/agents/halt-category`                      |
| MCP JSON-RPC entry                                    | `POST /v1/agents/mcp` (see MCP Server API Reference) |

{% hint style="warning" %}
`POST /v1/agents/register` and `POST /v1/agents/{agent_id}/propose` are marked **deprecated** in the spec and **return 404** today. Register external agents via `POST /v1/execution/agents/register` (below), and propose actions through `POST /v1/agents/run` (which routes → resolves → dry-runs the §6 gate → proposes through the gated path).
{% endhint %}

### Create a Tenant Agent for Headless Decisions

The public headless path starts with an owner creating a tenant-owned agent. This is not an MCP on-chain registration. It creates an active tenant agent and returns one exchange-only `brain_ak_...` credential in `api_key_once`.

```http
POST /v1/agents
Authorization: Bearer <owner_jwt>
Content-Type: application/json

{
  "kind": "tenant",
  "name": "Ops Agent",
  "purpose": "Request payment authorization decisions",
  "autonomy_preset": "ask_always"
}
```

Response (`201 Created`):

```json
{
  "agent": {
    "id": "agent_...",
    "kind": "tenant",
    "state": "active",
    "name": "Ops Agent"
  },
  "api_key_once": "brain_ak_test_...",
  "exchange": {
    "token_endpoint": "https://staging-auth.robotmoney.com/token",
    "resource": "https://staging-api.robotmoney.com/",
    "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
    "subject_token_type": "urn:brain:params:oauth:token-type:agent-api-key",
    "scope": "payment_intent:propose",
    "key_display": "once"
  }
}
```

The agent exchanges the one-time key for a five-minute JWT and calls `POST /v1/authorizations` with `execution: "external"`. The key does not grant `payment_intent:execute` and cannot be used directly as a bearer at resource routes.

### Register an MCP Agent

MCP agents are registered by an `execution:admin` caller. RobotMoney stores the record in a `pending_onchain` state; the caller supplies the identity fields and, optionally, the on-chain attestation references (`scope_hash`, `onchain_address`, `registered_tx`) once the `BrainMCPAgentRegistry` write exists.

```http
POST /v1/execution/agents/register
Authorization: Bearer <tenant token>
Content-Type: application/json

{
  "agent_id":        "ag_reconciliation_v1",
  "role":            "reconciliation",
  "display_name":    "Reconciliation Agent",
  "scope_hash":      "abc123...",
  "onchain_address": "0xagent...",
  "registered_tx":   "0x..."
}
```

`agent_id`, `role`, and `display_name` are required; `scope_hash` (hex), `onchain_address`, and `registered_tx` are optional.

Response (`201 Created`):

```json
{
  "id": "ag_reconciliation_v1",
  "kind": "external",
  "role": "reconciliation",
  "display_name": "Reconciliation Agent",
  "scope_hash": "abc123...",
  "onchain_address": "0xagent...",
  "state": "pending_onchain",
  "registered_tx": "0x...",
  "registered_at": null
}
```

The agent then connects over MCP using a JWT whose `scope_hash` claim must equal the `scope_hash` stored on-chain. The MCP server verifies that match on every call.

### List the First-Party Agent Catalog

`GET /v1/agents` returns the **internal** first-party agent definitions (capability, category, default-enabled state). Not the external-agent registry.

```http
GET /v1/agents?category=business&state=enabled
Authorization: Bearer <token>
```

```json
{
  "agents": [
    {
      "agent_key": "collections",
      "provenance": "first_party",
      "category": "business",
      "capabilities": ["invoice_followup", "dunning"],
      "enabled_by_default": true
    }
  ]
}
```

Filters: `kind`, `capability`, `category` (`business | consumer | agnostic`), `state` (`enabled | disabled`).

### Get an Agent

```http
GET /v1/agents/{agent_id}
Authorization: Bearer <token>
```

Returns `{ "definition": <catalog def with` shadow\_mode`>, "registration": null }`. The on-chain registration join is not wired yet, so `registration` is always `null` today; the `BrainMCPAgentRegistry` reader is a pending follow-up.

### Route an Event

The router scores candidate agents by capability + tenant scope grants + evidence and returns the best one. Routing is advisory. The selected agent still proposes through the gated path. The selection is itself an audit event.

```http
POST /v1/agents/route
Authorization: Bearer <token>
Content-Type: application/json

{
  "event":   "invoice.overdue",
  "context": { "invoice_id": "inv_8231", "counterparty_id": "cp_x" }
}
```

Provide `event` (a domain-event name) **or** `intent` (free-form text), plus optional `context`. Tenant-equality required.

```json
{
  "selected_agent_id": "collections",
  "fallback_agent_ids": [],
  "confidence": 0.92,
  "evidence_score": 1,
  "policy_status": "routed",
  "execution_mode": "propose",
  "reason": "selected collections (confidence 0.92)"
}
```

| Field                | Meaning                                                              |
| -------------------- | -------------------------------------------------------------------- |
| `selected_agent_id`  | The chosen agent, or `null` when nothing matches                     |
| `fallback_agent_ids` | Other eligible agents, best first                                    |
| `confidence`         | Router confidence in the selection (0..1)                            |
| `evidence_score`     | Fraction of the agent's required evidence that is present (0..1)     |
| `policy_status`      | `routed`, `unscoped` (matched but tenant scoped none), or `no_match` |
| `execution_mode`     | `execute`, `propose`, `confirm`, `notify_only`, `reject`, or `null`  |

### Run an Agent End-to-End

The full route → resolve action → dry-run §6 gate → persist `agent_runs` row → propose pipeline. **Money-movers are shadowed by default**. A financial proposal from an un-promoted agent terminates as `shadow_completed` and moves no money. Going live is a deliberate per-agent promotion with strict caps + allowlisted rails.

```http
POST /v1/agents/run
Authorization: Bearer <token>
Content-Type: application/json

{ "event": "invoice.overdue", "context": { "invoice_id": "inv_8231" } }
```

```json
{
  "status": "proposal_created",
  "routing_decision_id": "rd_001",
  "run_id": "run_001",
  "selected_agent_id": "collections",
  "action": "draft_followup",
  "shadow_mode": false,
  "proposed": { "id": "pi_a1b2c3", "status": "pending_approval", "policy_decision_id": "pd_7331" },
  "reason": "matched dunning rule for inv_8231"
}
```

A proposal-layer idempotency collision returns `409` with `agent_proposal_duplicate`.

### Enqueue an Event (async)

```http
POST /v1/agents/events
Authorization: Bearer <token>
Content-Type: application/json

{ "event": "invoice.overdue", "context": { "invoice_id": "inv_8231" } }
```

```json
{ "job_id": "job_001", "status": "queued" }
```

### Routing Decisions & Run History

| Endpoint                                  | Purpose                                                                             |
| ----------------------------------------- | ----------------------------------------------------------------------------------- |
| `GET /v1/agents/routing-decisions/{id}`   | Routing decision detail                                                             |
| `GET /v1/agents/runs`                     | List runs (filter `agent_id`, `status`, `category`, `limit`)                        |
| `GET /v1/agents/runs/{run_id}`            | Run summary (`status` ∈ `completed`, `failed`, `shadow_completed`, `rejected`)      |
| `GET /v1/agents/runs/{run_id}/why`        | Structured reason + (redacted) reasoning trace + candidate agents + `behavior_hash` |
| `GET /v1/agents/runs/{run_id}/evidence`   | Evidence the run consulted                                                          |
| `GET /v1/agents/runs/{run_id}/gate-trace` | The §6 gate-check rows for the run's PaymentIntent                                  |
| `GET /v1/agents/runs/{run_id}/proof`      | Proxy to the canonical Proof artifact for the run's PaymentIntent                   |
| `GET /v1/agents/{agent_id}/actions`       | All actions a given agent produced (proposal + payment\_intent + status)            |

### Kill-Switch

| Endpoint                                               | Purpose                                                                       |
| ------------------------------------------------------ | ----------------------------------------------------------------------------- |
| `POST /v1/agents/{agent_id}/halt`                      | Pause every in-flight intent for the agent and set its state to `quarantined` |
| `POST /v1/agents/{agent_id}/restore`                   | Restore a quarantined agent record to `active`                                |
| `POST /v1/agents/{agent_id}/contribution-hold/release` | Clear the agent's contribution hold so later contributions can extract        |
| `POST /v1/agents/halt-category`                        | Emergency-stop every agent in a category. Body `{ "category": "business" }`   |

Both routes are tenant-root and emit audit events. Halting an agent atomically pauses its in-flight PaymentIntents (the rail dispatcher re-reads state immediately before submission and aborts cleanly if the intent was paused).

### What's Next

<table data-view="cards"><thead><tr><th></th><th></th><th data-type="content-ref"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Authorization Decisions</strong></td><td>The decision-only endpoint tenant agents call.</td><td><a href="/govern/authorizations-api.md">Authorization Decisions</a></td><td></td></tr><tr><td><strong>Agent Key Exchange</strong></td><td>How <code>brain_ak_...</code> keys become five-minute JWTs.</td><td><a href="/how-it-works/agent-key-exchange.md">Agent Key Exchange</a></td><td></td></tr><tr><td><strong>Payment Intents</strong></td><td>The Ledger entity MCP and internal agents propose for execution.</td><td><a href="/platform/payment-intents-api.md">Payment Intents API</a></td><td></td></tr><tr><td><strong>BrainMCPAgentRegistry</strong></td><td>The on-chain registry for MCP agents.</td><td><a href="/smart-contracts/brainmcpagentregistry.md">BrainMCPAgentRegistry</a></td><td></td></tr></tbody></table>
