> 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/how-it-works/agents.md).

# Agents and Agent Keys

The Agent Layer coordinates **tenant agents, internal agents, and MCP agents**. RobotMoney ships a small set of internal agents for platform workflows. A tenant can also create a headless agent, receive a one-time `brain_ak_...` key, exchange it for a five-minute JWT, and call `POST /v1/authorizations` with `execution: "external"`. MCP agents use the on-chain registry and SIWX path.

{% hint style="info" %}
RobotMoney does not need to build every agent. **It is the substrate they share.** External agents listed on RobotMoney do not need to ship their own ledger, memory, policy engine, or audit pipeline.
{% endhint %}

{% hint style="info" %}
**Internal agents are first-class participants of this same layer.** A RobotMoney-shipped agent registers in the same `BrainMCPAgentRegistry` and carries a scope-hash commitment. The only difference is a `provenance: "internal"` metadata field and that RobotMoney operates the execution key. There is no separate native-agent path. See [Internal agents](/concepts/internal-agents.md).
{% endhint %}

### Agents Are First-Class Entities

MCP agents have four attributes registered on-chain or referenced from on-chain. Tenant-created headless agents are lighter: the owner creates them through `POST /v1/agents`, and the returned agent key is exchange-only.

| Attribute              | What It Is                                                                                                                                            |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Identity**           | A `BrainMCPAgentRegistry` record on Base, keyed by an agent address (`agentId`/`tenantId`/`scopeHash`/`behaviorHash`)                                 |
| **Capability set**     | Declared at registration: `pay_invoice`, `rebalance_treasury`, `file_vat_return`, etc                                                                 |
| **Reputation history** | A per-agent reputation pointer / Merkle root in `BrainReputationRegistry` (RFC 0001, **UNAUDITED testnet**); read by Policy as a threshold input only |
| **Scope hash**         | A commitment to the agent's sorted plain scope strings, registered as `scopeHash`                                                                     |

### Discovery and Routing

Tenants and other agents query the registry by capability and reputation.

```
Tenant: "I need to pay an invoice"
   ↓
RobotMoney queries BrainMCPAgentRegistry
   ↓
Returns agents with capability = pay_invoice
   ↓
RobotMoney selects based on:
   - capability match
   - policy compatibility
   - cost
   - historical performance (on-chain reputation via BrainReputationRegistry. RFC 0001, testnet)
   ↓
Selection itself is an audited event
```

[**→ Smart contract reference**](/smart-contracts/overview.md)

#### Category-aware selection

Some triggers match agents of different categories. `cash.balance_high`, for example, matches a business **Treasury** agent and a consumer **Savings** agent. The router resolves the tenant's category (business or consumer) and adds it to scoring: a candidate whose category matches the tenant is preferred, so a business tenant routes to Treasury and a consumer tenant routes to Savings.

A category mismatch is a **scoring downgrade, not a hard reject**. A mismatched agent can still be selected when it is the best or only match, so an explicit user intent overrides the default category preference. Agnostic agents (which serve both categories) take no penalty, and when no tenant category is resolved the router is category-blind.

#### Intent matching

When a request carries a free-form intent rather than a domain event, the router scores it against each agent's declared `intent_patterns`. Two classifier strategies share one interface, selected by the `AGENT_INTENT_CLASSIFIER` flag:

* **`rules`** (default). A deterministic token-overlap classifier. Fast and dependency-free, but it only matches phrasings that share words with a pattern.
* **`embedding`**. Embeds the intent and the patterns and scores by cosine similarity, so paraphrases match ("chase late-paying customers" routes to Collections even though it shares no tokens with "follow up on overdue invoice"). Pattern embeddings are cached and reindexed when the catalog changes.

The embedding classifier keeps the rules classifier as a **live fallback**: when an intent scores below the similarity threshold (or the embedding service is unavailable) the router falls back to token overlap. Selection scoring is unchanged. Only the source of the intent-match score differs.

### Three Execution Paths

Approved actions execute through one of three paths.

<table data-view="cards"><thead><tr><th></th><th></th><th data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>🏦 Off-chain Rail</strong></td><td>A bank API or processor SDK called server-side by RobotMoney on behalf of the tenant.</td><td></td></tr><tr><td><strong>⛓️ On-chain Via Smart Account</strong></td><td>The agent's granted session key calls <code>BrainSmartAccount.executeViaSessionKey</code>, enforced on-chain against its spend caps, bound <code>policyVersion</code>, and replay nonce.</td><td></td></tr><tr><td><strong>🤝 Agent-to-Agent</strong></td><td>An agent invokes another agent's capability through RobotMoney. Both calls are policy-checked. Both are audited.</td><td></td></tr></tbody></table>

### Settlement: When Agents Are Paid

Where an external agent is paid for its work, RobotMoney coordinates settlement so that the operator never redirects funds.

| Pattern                    | Mechanism                         | Use Case                                                                 |
| -------------------------- | --------------------------------- | ------------------------------------------------------------------------ |
| **Escrowed jobs**          | `BrainEscrow` (UNAUDITED testnet) | Multi-step work where USDC releases incrementally as milestones complete |
| **HTTP-native settlement** | x402                              | Per-call pay-per-use (an agent paying for an API call or tool call)      |

For x402 the tenant's smart account / EOA pays and the agent's address receives. For conditional work, the immutable `BrainEscrow` contract escrows USDC against a hashed job commitment and releases/refunds incrementally. The arbiter can only ever pay the designated payee or refund the designated payer, never redirect. RobotMoney records and proves the flow.

**→ Escrow and x402 reference**

### Agent Key Authentication

Tenant-created headless agents authenticate with agent API keys. The key itself is not a bearer token. The agent exchanges it at the discovered token endpoint for a short-lived JWT scoped to `payment_intent:propose`.

```
1. Owner session calls POST /v1/agents
2. RobotMoney returns api_key_once and exchange metadata
3. Agent exchanges the key at /token
4. Auth returns a five-minute JWT
5. Agent calls POST /v1/authorizations
```

The exchanged token is propose-only. It cannot execute, approve, or move money.

### SIWX Authentication for MCP

MCP agents authenticate using SIWX (Sign-In With X), based on EIP-4361 over Base.

```
1. RobotMoney issues a structured SIWX challenge to the agent
2. Agent signs the challenge with its registered execution key
3. RobotMoney verifies the signature and recovers the agent address
4. RobotMoney looks up the address in BrainMCPAgentRegistry
5. RobotMoney verifies the agent is active and its stored scope hash matches BrainMCPAgentRegistry
6. RobotMoney issues a session token with scoped capabilities
```

The session token gates every subsequent API or MCP call. Each tool checks the scope required for that operation before it runs.

### Scope Hash Commitment

The current agent scope commitment is `scopeHash`, not a structured EIP-712 attestation. RobotMoney sorts the agent's plain scope strings lexicographically, joins them with `|`, then calculates `keccak256` over the result. The MCP verifier fails closed when the stored hash does not match the hash registered in `BrainMCPAgentRegistry`.

The current agent API does not yet read and return an agent's on-chain registration record. It returns `registration: null` until that reader is wired. There is no public API for per-scope deletion or agent deactivation. Operators can halt an agent with `POST /v1/agents/{agent_id}/halt`, which pauses its approved in-flight payment intents and quarantines its local record, then restore a quarantined record with `POST /v1/agents/{agent_id}/restore`.

### What RobotMoney Provides vs What the Agent Provides

| Concern                             | RobotMoney Provides                                                                    | Agent Provides |
| ----------------------------------- | -------------------------------------------------------------------------------------- | :------------: |
| **Verified financial context**      | ✅ Wiki + Ledger + citations                                                            |                |
| **Policy enforcement**              | ✅ Off-chain + on-chain                                                                 |                |
| **Identity and scope**              | ✅ `BrainMCPAgentRegistry`; reputation in `BrainReputationRegistry` (RFC 0001, testnet) |                |
| **Audit trail**                     | ✅ Hash chain + Merkle anchor                                                           |                |
| **Settlement infrastructure**       | ✅ Smart account, `BrainEscrow` (testnet), x402                                         |                |
| **Domain logic for the capability** |                                                                                        |        ✅       |
| **The actual work**                 |                                                                                        |        ✅       |

{% hint style="success" %}
This is why the Agent Layer is open. The substrate is general-purpose. The capabilities are pluggable.
{% endhint %}

### 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>📐 Policy</strong></td><td>How agent actions are gated.</td><td><a href="/how-it-works/policy-and-permissioning.md">Policy and Permissioning</a></td><td></td></tr><tr><td><strong>📜 BrainSmartAccount</strong></td><td>The session-key smart account that enforces spend caps and policy binding on every call.</td><td><a href="/smart-contracts/brainsmartaccount.md">BrainSmartAccount</a></td><td></td></tr><tr><td><strong>📜 BrainMCPAgentRegistry</strong></td><td>The agent identity contract.</td><td><a href="/smart-contracts/brainmcpagentregistry.md">BrainMCPAgentRegistry</a></td><td></td></tr></tbody></table>
