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.
This page is optimized for AI agent consumption. For human-friendly guides, see the Documentation.
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.
- Call
/validate before EVERY transaction. No exceptions.
- If
allowed: false, do NOT execute. Display the blockReason to the user.
- If the API is unreachable, do NOT execute. Do NOT fall back to the wallet directly.
- Display validation results to the user BEFORE any wallet call.
- Even with custodial wallets, validate with Mandate BEFORE calling the wallet API.
- Skipping validation bypasses spend limits, allowlists, and approval workflows the owner configured.
Fail-Safe Behavior
When the Mandate API is unreachable:
- Do NOT execute the transaction.
- Do NOT fall back to calling the wallet API directly.
- Display:
Mandate: policy server unreachable, transaction halted for safety
- Retry up to 3 times at 30-second intervals.
- 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).
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
{ "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.
Registration: POST /agents/register
No auth required. Creates an agent identity and returns credentials.
curl -X POST https://app.mandate.md/api/agents/register \
-H "Content-Type: application/json" \
-d '{"name":"MyAgent","evmAddress":"0xYourAddress","chainId":84532}'
Response
{
"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.
Activation: POST /activate
Set the EVM address for a registered agent. Call once after registration. Requires auth.
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.
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.
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 and Common Errors.
SDK Error Types
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.
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 and 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.
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 or Policy Builder.
| 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.
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 and Prompt Injection Defense.
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 |
| Claude Code | claude plugin:install claude-mandate-plugin | Claude Code |
| GOAT SDK | @mandate.md/goat-plugin | GOAT SDK |
| AgentKit | @mandate.md/agentkit-provider | AgentKit |
| ElizaOS | @mandate.md/eliza-plugin | ElizaOS |
For all integrations, see 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.