# AgentShield Agent API

All authenticated requests use `Authorization: Bearer $AGENTSHIELD_GRANT`. The helper adds this header. Amounts are exact decimal strings in the token's smallest unit.

## Balances

Commands: `balances`, `refresh-balances`

Every valid Agent grant may call these read-only endpoints; no extra scope is required. `balances` returns native balances, curated ERC-20 balances, and exact-contract assets previously encountered in a successful LI.FI quote. It is cached for up to 60 seconds. `refresh-balances` uses `POST` to bypass that cache; simultaneous reads are coalesced, and each supported chain is read through one Multicall RPC request. Both return exact `amount_atomic` and formatted `amount`; neither requires the wallet to be unlocked. Discovered assets remain exact addresses, never new ticker aliases.

## Curated assets

Commands: `assets`, `lifi-assets <chain-id>`, `lifi-stablecoins`

Call `assets` before constructing a route. It returns the chain-specific canonical asset aliases, their exact addresses, decimals, and source. A swap or bridge accepts a listed symbol such as `USDC`, `USDT`, or `USDG`; it resolves to that chain's maintained address. For an asset not in the list, provide its exact ERC-20 contract address. Bare unknown symbols are rejected rather than guessed.

`lifi-assets <chain-id>` returns LI.FI's live full catalog for a configured chain, including indexed MEME tokens. `lifi-stablecoins` returns its live `stablecoin`-tagged catalog. The full LI.FI catalog is also available to SDK integrations with `client.assets({ catalog: "lifi", chainId })`. LI.FI catalog entries are discovery data (`alias_allowed: false`): use their exact `address`, never their symbol, in a route request.

```json
{
  "assets": [
    {"chain_id": 42161, "symbol": "ETH", "address": "native", "decimals": 18},
    {"chain_id": 42161, "symbol": "USDC", "address": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", "decimals": 6}
  ]
}
```

## Requests

### Message signing

Command: `sign-message`

```json
{
  "message": "Approve invoice 42 for internal reconciliation"
}
```

### Structured transaction signing

Command: `sign-payload`

```json
{
  "payload": {
    "chain_id": 1,
    "nonce": 7,
    "gas_limit": 100000,
    "to": "0x0000000000000000000000000000000000000000",
    "value_wei": "0",
    "data": "0x",
    "gas_price_wei": "1000000000"
  },
  "idempotency_key": "task-42-sign-1",
  "reason": "Sign the reviewed EVM transaction for task 42"
}
```

Use either `gas_price_wei` or the EIP-1559 fields `max_fee_per_gas_wei` and `max_priority_fee_per_gas_wei`.

### Send

Command: `send`

```json
{
  "chain_id": 1,
  "to": "0x0000000000000000000000000000000000000000",
  "value_wei": "1000000",
  "token": "0x0000000000000000000000000000000000000000",
  "idempotency_key": "invoice-42-send-1",
  "reason": "Pay invoice 42"
}
```

Use `"token": "native"` for the chain's native asset.

### Swap quote and swap

Commands: `swap-quote`, `swap`

```json
{
  "from_chain": "1",
  "to_chain": "1",
  "from_token": "USDC",
  "to_token": "native",
  "from_amount": "1000000",
  "slippage": 0.005,
  "idempotency_key": "task-42-swap-1",
  "reason": "Swap the approved task budget"
}
```

Omit `idempotency_key` and `reason` for `swap-quote`. `slippage` is a decimal fraction and must not exceed `0.05`.

### Bridge quote and bridge

Commands: `bridge-quote`, `bridge`

```json
{
  "from_chain": "1",
  "to_chain": "42161",
  "from_token": "USDC",
  "to_token": "USDC",
  "from_amount": "1000000",
  "to_address": "0x0000000000000000000000000000000000000000",
  "slippage": 0.005,
  "idempotency_key": "task-42-bridge-1",
  "reason": "Move the approved task budget to Arbitrum"
}
```

Omit `idempotency_key` and `reason` for `bridge-quote`.

### Create a bounded child authorization

Command: `delegate-grant`

The current token must contain `grant:delegate`. The child is always bound to the same wallet. Every child scope must already exist on the parent, every explicit policy value must be equal or stricter, omitted bounded fields inherit the parent restriction, and the child expiry must be earlier than the parent expiry. Revoking a parent also revokes all of its descendants.

The bound wallet must be selected and unlocked when a root or delegated authorization is created. AgentShield returns `423 WALLET_LOCKED` otherwise. Unlocking always remains a local user action; an Agent must never request the wallet PIN.

```json
{
  "label": "Settlement Agent",
  "scopes": ["wallet:read", "wallet:send"],
  "ttl_seconds": 7200,
  "allow_direct_execution": false,
  "policy": {
    "allowed_chain_ids": [42161],
    "max_amount_usd": "10",
    "allowed_tokens": ["0xaf88d065e77c8cC2239327C5EDb3A432268e5831"],
    "max_operations_per_day": 5
  }
}
```

`ttl_seconds` must be positive. The response contains the child `grant` metadata and a `cag_` token shown only once. Do not automatically retry this request after a timeout. A parent that requires local approval cannot create a direct-execution child. A child may receive `grant:delegate` only if the parent has it; every later generation is checked by the same rules.

## Operation lifecycle

Transaction signing, Send, Swap, and Bridge normally return HTTP 202 with an operation ID:

```json
{
  "id": "op_example",
  "kind": "send",
  "state": "pending_approval"
}
```

Retrieve it with `node scripts/agentshield.mjs operation op_example`.

- `pending_approval`: wait for local AgentShield approval.
- `executing`: signing or provider execution is in progress.
- `completed`: terminal success for a signing-only operation; no transaction was broadcast.
- `submitted`: broadcast or provider submission completed; settlement may still be pending.
- `confirmed`: terminal success when supported by the provider.
- `rejected`, `failed`, `cancelled`: terminal failure states.
- `attention_required`: stop and request manual review; do not retry automatically.

## Agent approval

Commands: `approvals`, `approve <operation-id>`

An authorization containing `wallet:approve` may list pending operations for its own wallet and approve one of them. The wallet must currently be selected and unlocked. AgentShield never exposes pending operations from another wallet, even if the caller knows their IDs.

Before approval, call `approvals` and inspect the complete `payload` and `reviews`. Approve only when the user explicitly authorized the decision and the recipient, chain, token, amount, contract, and reason all match that instruction.

```bash
node scripts/agentshield.mjs approvals
node scripts/agentshield.mjs approve op_example
```

## Grant scopes

- `wallet:read`
- `wallet:sign_message`
- `wallet:sign_transaction`
- `wallet:send`
- `wallet:swap`
- `wallet:bridge`
- `wallet:approve`
- `grant:delegate`

Ask the user to issue only the scopes required for the task. A transaction grant may require local approval or allow policy-compliant direct execution, as selected by the user; unavailable price data still forces human review. Chain, amount, frequency, recipient, token, and contract policy fields are optional restrictions on a root grant; an empty field means unrestricted. For delegated children, an omitted field inherits any restriction already present on the parent.

## Network readiness

`status` returns `chain_ids` for the EVM networks currently configured in AgentShield. The standalone service includes public RPC failover for its standard mainnets, while local service configuration may override individual chains or disable public RPCs entirely. If a requested chain is absent, ask the user to configure that chain locally; do not ask for an RPC by default before checking status.
