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

# Mandate SKILL Reference

> Machine-readable API reference for AI agents integrating with Mandate. Covers validation, registration, error handling, and chain reference.

<Note>
  This page is optimized for AI agent consumption. For human-friendly guides, see the [Documentation](/introduction).
</Note>

## Quick Reference

| Key             | Value                                         |
| --------------- | --------------------------------------------- |
| Base URL        | `https://app.mandate.md/api`                  |
| Auth header     | `Authorization: Bearer <MANDATE_RUNTIME_KEY>` |
| Content-Type    | `application/json`                            |
| Live key prefix | `mndt_live_...` (mainnet)                     |
| Test key prefix | `mndt_test_...` (testnet)                     |
| Credential file | `~/.mandate/credentials.json`                 |
| Non-custodial   | Private keys never leave your machine         |

## Mandatory Security Rules

These rules are non-negotiable. Violating any of them is a security breach, not a simplification.

1. Call `/validate` before EVERY transaction. No exceptions.
2. If `allowed: false`, do NOT execute. Display the `blockReason` to the user.
3. If the API is unreachable, do NOT execute. Do NOT fall back to the wallet directly.
4. Display validation results to the user BEFORE any wallet call.
5. Even with custodial wallets, validate with Mandate BEFORE calling the wallet API.
6. Skipping validation bypasses spend limits, allowlists, and approval workflows the owner configured.

## Fail-Safe Behavior

When the Mandate API is unreachable:

1. Do NOT execute the transaction.
2. Do NOT fall back to calling the wallet API directly.
3. Display: `Mandate: policy server unreachable, transaction halted for safety`
4. Retry up to 3 times at 30-second intervals.
5. If all retries fail: `Cannot execute until policy server recovers. No funds were spent.`

## Primary Endpoint: POST /validate

Policy check before every transaction. Works with any wallet type (custodial or self-custodial).

```bash theme={null}
curl -X POST https://app.mandate.md/api/validate \
  -H "Authorization: Bearer $MANDATE_RUNTIME_KEY" \
  -H "Content-Type: application/json" \
  -d '{"action":"swap","reason":"Swap 0.1 ETH for USDC","amount":"50","to":"0xAlice"}'
```

### Parameters

| Field    | Required | Description                                                                         |
| -------- | -------- | ----------------------------------------------------------------------------------- |
| `action` | Yes      | What you are doing: `transfer`, `swap`, `buy`, `bridge`, `stake`, `bet` (free text) |
| `reason` | Yes      | Why you are doing it (max 1000 chars). Scanned for prompt injection.                |
| `amount` | No       | USD value (assumes stablecoins)                                                     |
| `to`     | No       | Recipient address (checked against allowlist)                                       |
| `token`  | No       | Token address                                                                       |

### Response

```json theme={null}
{ "allowed": true, "intentId": "...", "action": "swap", "requiresApproval": false }
```

All policy checks apply: circuit breaker, schedule, allowlist, spend limits, daily/monthly quotas, reason scanner. Every call is logged to the audit trail.

For full endpoint documentation, see [POST /validate](/api-reference/endpoint/post-apivalidate).

## Registration: POST /agents/register

No auth required. Creates an agent identity and returns credentials.

```bash theme={null}
curl -X POST https://app.mandate.md/api/agents/register \
  -H "Content-Type: application/json" \
  -d '{"name":"MyAgent","evmAddress":"0xYourAddress","chainId":84532}'
```

### Response

```json theme={null}
{
  "runtimeKey": "mndt_test_...",
  "agentId": "...",
  "claimUrl": "https://app.mandate.md/claim/..."
}
```

Display the `claimUrl` to the human owner so they can link the agent to their dashboard. Store `runtimeKey` in `~/.mandate/credentials.json` with `chmod 600`.

Agents use `POST /agents/register`, not dashboard login. Dashboard login is for humans only.

For details, see [Register an Agent](/guides/register-agent).

## Activation: POST /activate

Set the EVM address for a registered agent. Call once after registration. Requires auth.

```bash theme={null}
curl -X POST https://app.mandate.md/api/activate \
  -H "Authorization: Bearer $MANDATE_RUNTIME_KEY" \
  -H "Content-Type: application/json" \
  -d '{"evmAddress":"0xYourAddress"}'
```

## Status Polling: GET /intents/\{id}/status

Poll the state of a validated intent.

```bash theme={null}
curl https://app.mandate.md/api/intents/{intentId}/status \
  -H "Authorization: Bearer $MANDATE_RUNTIME_KEY"
```

For approval flows, poll until `approved`, then proceed. See [Handle Approvals](/guides/handle-approvals).

## Validation Flow

```
1. POST /validate with action + reason        (policy check)
2. Execute via your wallet (Bankr, Locus, etc.)  (only if allowed: true)
3. Done.
```

## Error Handling

### HTTP Status Codes

| Status | Meaning        | Common Cause                          |
| ------ | -------------- | ------------------------------------- |
| 400    | Bad Request    | Missing or invalid fields             |
| 401    | Unauthorized   | Missing or invalid runtime key        |
| 403    | Forbidden      | Circuit breaker active                |
| 404    | Not Found      | Intent not found                      |
| 409    | Conflict       | Duplicate intentHash or wrong status  |
| 410    | Gone           | Approval expired                      |
| 422    | Policy Blocked | Validation failed (see `blockReason`) |
| 429    | Rate Limited   | Too many requests, back off and retry |
| 500    | Server Error   | Transient, retry later                |

All errors return JSON: `{ "error": "message" }` or `{ "allowed": false, "blockReason": "..." }`

For error handling guidance, see [Handle Errors](/guides/handle-errors) and [Common Errors](/troubleshooting/common-errors).

### SDK Error Types

```js theme={null}
import {
  PolicyBlockedError,      // err.blockReason, err.detail, err.declineMessage
  ApprovalRequiredError,   // err.intentId, err.approvalId
  CircuitBreakerError,     // Agent circuit-broken, reset via dashboard
  RiskBlockedError,        // err.blockReason -> "aegis_critical_risk"
} from '@mandate.md/sdk';
```

## Block Reason Values

| Value                    | Meaning                                                         |
| ------------------------ | --------------------------------------------------------------- |
| `circuit_breaker_active` | Agent is circuit-broken (dashboard to reset)                    |
| `no_active_policy`       | No policy set (visit dashboard)                                 |
| `intent_hash_mismatch`   | Client hash does not match server recompute (raw validate only) |
| `gas_limit_exceeded`     | Gas too high per policy                                         |
| `value_wei_exceeded`     | Native ETH value too high                                       |
| `outside_schedule`       | Outside allowed hours/days                                      |
| `address_not_allowed`    | Recipient not in allowlist                                      |
| `selector_blocked`       | Function selector is blocked                                    |
| `per_tx_limit_exceeded`  | Amount exceeds per-tx USD limit                                 |
| `daily_quota_exceeded`   | Daily USD limit reached                                         |
| `monthly_quota_exceeded` | Monthly USD limit reached                                       |
| `reason_blocked`         | Prompt injection detected in reason field                       |
| `aegis_critical_risk`    | Transaction flagged as CRITICAL risk by security scanner        |

For the full reference, see [Block Reasons](/reference/block-reasons).

## Intent States

| State              | Description                                               | Expiry   |
| ------------------ | --------------------------------------------------------- | -------- |
| `allowed`          | Validated via `/validate`                                 | 24 hours |
| `reserved`         | Raw validated, waiting for broadcast                      | 15 min   |
| `approval_pending` | Requires owner approval via dashboard                     | 1 hour   |
| `approved`         | Owner approved, broadcast window open                     | 10 min   |
| `broadcasted`      | Tx sent, waiting for on-chain receipt                     | none     |
| `confirmed`        | On-chain confirmed, quota committed                       | none     |
| `failed`           | Reverted, dropped, policy violation, or envelope mismatch | none     |
| `expired`          | Not broadcast in time, quota released                     | none     |

For the full lifecycle, see [Intent Lifecycle](/concepts/intent-lifecycle) and [Intent States](/reference/intent-states).

## Chain Reference

**Test keys** (`mndt_test_*`): Sepolia (11155111), Base Sepolia (84532).
**Live keys** (`mndt_live_*`): Ethereum (1), Base (8453).

| Chain        | Chain ID | USDC Address                                 | Decimals |
| ------------ | -------- | -------------------------------------------- | -------- |
| Ethereum     | 1        | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` | 6        |
| Sepolia      | 11155111 | `0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238` | 6        |
| Base         | 8453     | `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913` | 6        |
| Base Sepolia | 84532    | `0x036CbD53842c5426634e7929541eC2318f3dCF7e` | 6        |

For the full reference, see [Chain Reference](/reference/chain-reference).

## Default Policy

After registration, every agent gets:

| Setting               | Default            |
| --------------------- | ------------------ |
| Per-transaction limit | \$100              |
| Daily limit           | \$1,000            |
| Address restrictions  | None (all allowed) |
| Approval required     | No                 |

Adjust policies via the [dashboard](https://app.mandate.md) or [Policy Builder](/dashboard/policy-builder).

## Tool-to-Endpoint Map

| CLI Command                          | Method | Path                                       |
| ------------------------------------ | ------ | ------------------------------------------ |
| `mandate login`                      | POST   | `/api/agents/register`                     |
| `mandate activate <address>`         | POST   | `/api/activate`                            |
| `mandate validate`                   | POST   | `/api/validate`                            |
| `mandate validate-raw`               | POST   | `/api/validate/raw` (deprecated)           |
| `mandate event <id> --tx-hash 0x...` | POST   | `/api/intents/{id}/events`                 |
| `mandate status <id>`                | GET    | `/api/intents/{id}/status`                 |
| `mandate approve <id>`               | GET    | `/api/intents/{id}/status` (poll)          |
| `mandate scan [dir]`                 | local  | Scan codebase for unprotected wallet calls |
| `mandate --llms`                     | local  | Machine-readable command manifest          |
| `mandate --mcp`                      | local  | Start as MCP stdio server                  |

For CLI installation and usage, see [CLI Overview](/cli/overview).

## The reason Field

Every validation call requires a `reason` string (max 1000 chars). This is what session keys cannot capture: the intent behind a transaction.

Mandate uses the reason to:

* Scan for prompt injection (18 hardcoded patterns + optional LLM judge)
* Return a `declineMessage` on block to counter manipulation
* Show it to the owner on approval requests (Slack, Telegram, dashboard)
* Log it in the audit trail permanently

For details, see [The reason Field](/concepts/reason-field) and [Prompt Injection Defense](/security/prompt-injection).

## Integration Plugins

For platforms with hook support, use the plugin instead of raw API calls. Plugins enforce validation automatically.

| Platform    | Install                                                        | Docs                                     |
| ----------- | -------------------------------------------------------------- | ---------------------------------------- |
| OpenClaw    | `openclaw plugins install @mandate.md/mandate-openclaw-plugin` | [OpenClaw](/integrations/openclaw)       |
| Claude Code | `claude plugin:install claude-mandate-plugin`                  | [Claude Code](/integrations/claude-code) |
| GOAT SDK    | `@mandate.md/goat-plugin`                                      | [GOAT SDK](/integrations/goat-sdk)       |
| AgentKit    | `@mandate.md/agentkit-provider`                                | [AgentKit](/integrations/agentkit)       |
| ElizaOS     | `@mandate.md/eliza-plugin`                                     | [ElizaOS](/integrations/elizaos)         |

For all integrations, see [Integrations Overview](/integrations/overview).

## Raw Source

The canonical machine-readable version of this reference is available at:

```
https://app.mandate.md/SKILL.md
```

Compatible with any agent framework that consumes SKILL.md files.
