What is MandateClient?
MandateClient is the low-level API wrapper in the Mandate SDK. It maps 1:1 to the Mandate REST API: validate transactions, post events, poll intent status, and wait for approval decisions. Every method returns typed results and throws typed errors.
Use MandateClient when your agent delegates signing to a separate wallet service (Bankr, Locus, Sponge) or when you need fine-grained control over the transaction lifecycle. If your agent holds its own private key, use MandateWallet instead: it wraps MandateClient and handles signing, broadcasting, and confirmation automatically.
Installation
- bun
- npm
- pnpm
Constructor
MandateConfig interface accepts two fields:
| Parameter | Type | Required | Description |
|---|---|---|---|
runtimeKey | string | Yes | Your mndt_live_... or mndt_test_... runtime key |
baseUrl | string | No | Mandate API base URL. Defaults to https://app.mandate.md |
Static Methods
MandateClient.register(params)
Registers a new agent with Mandate. This is the only method that does not require authentication, because the agent does not have a runtime key yet.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Human-readable agent name |
evmAddress | `0x${string}` | Yes | The agent’s wallet address |
chainId | number | Yes | Target chain (e.g. 84532 for Base Sepolia) |
defaultPolicy | object | No | Initial spend limits in USD |
baseUrl | string | No | Override the API base URL |
RegisterResult:
The
claimUrl must be shared with the wallet owner. They visit it to link the agent to their dashboard account. Until claimed, the agent operates under default policies.Instance Methods
client.validate(params)
The primary method for transaction validation. Sends the intended action to Mandate’s policy engine, which runs 14 sequential checks: circuit breaker, schedule, allowlist, blocked actions, per-transaction limits, daily and monthly quotas, risk screening, reputation scoring, reason scanning, and approval thresholds.
PreflightPayload:
PreflightResult:
| Error Class | HTTP Status | Condition |
|---|---|---|
PolicyBlockedError | 422 | Transaction violates a policy rule |
CircuitBreakerError | 403 | Agent is emergency-stopped |
ApprovalRequiredError | 202 | Amount exceeds approval threshold |
RiskBlockedError | 422 | Address flagged by risk scanner |
The
reason field is required. Mandate scans it for prompt injection patterns and displays it to the wallet owner on approval requests. Write a clear, honest description of what the agent is doing and why.client.preflight(params)
Backward-compatible alias for validate(). Same signature, same behavior, same return type.
validate() in new code. The preflight() method exists so older integrations continue to work without changes.
client.rawValidate(payload)
Deprecated. Use
validate() for action-based validation. Raw validation is kept for legacy self-custodial flows but will be removed in a future version. MandateWallet uses this method internally: you do not need to call it directly.intentHash that must match the server’s recomputation exactly.
IntentPayload:
ValidateResult:
validate(): PolicyBlockedError, CircuitBreakerError, ApprovalRequiredError, RiskBlockedError.
client.postEvent(intentId, txHash)
Posts the broadcast transaction hash back to Mandate for envelope verification. Required after rawValidate() flows. The server compares the on-chain transaction against the validated parameters. If they do not match, the circuit breaker trips.
| Parameter | Type | Description |
|---|---|---|
intentId | string | The intent ID returned by rawValidate() |
txHash | `0x${string}` | The broadcast transaction hash |
client.getStatus(intentId)
Returns the current state of an intent. Use this for one-off status checks. For continuous polling, use waitForApproval() or waitForConfirmation() instead.
IntentStatus:
client.waitForApproval(intentId, opts?)
Polls the intent status until the wallet owner approves, rejects, or the approval expires. Use this after catching an ApprovalRequiredError.
| Parameter | Type | Default | Description |
|---|---|---|---|
intentId | string | - | The intent ID from ApprovalRequiredError |
timeoutMs | number | 3,600,000 | Maximum wait time (1 hour matches server approval TTL) |
intervalMs | number | 5,000 | Polling interval |
onPoll | (status: IntentStatus) => void | - | Callback invoked on each poll |
IntentStatus with status: 'approved' or status: 'confirmed'.
Throws MandateError if the approval is rejected (failed), expires (expired), or the polling timeout is exceeded.
Example:
client.waitForConfirmation(intentId, opts?)
Polls the intent status until the transaction is confirmed on-chain. Use this after broadcasting a transaction to verify it landed.
| Parameter | Type | Default | Description |
|---|---|---|---|
intentId | string | - | The intent ID to monitor |
timeoutMs | number | 300,000 | Maximum wait time (5 minutes) |
intervalMs | number | 3,000 | Polling interval |
IntentStatus with status: 'confirmed', including the txHash, blockNumber, and gasUsed.
Throws MandateError if the transaction fails, expires, or the polling timeout is exceeded.
Example:
How does MandateClient compare to MandateWallet?
| Capability | MandateClient | MandateWallet |
|---|---|---|
| Validate transactions | Yes | Yes (internally) |
| Sign transactions | No | Yes (local private key) |
| Broadcast to chain | No | Yes |
| Post events | Yes (manual) | Yes (automatic) |
| Wait for confirmation | Yes (manual) | Yes (automatic) |
| Requires private key | No | Yes |
| Use case | Custodial wallets, custom signing | Self-custodial agents |
MandateClient when another service handles signing. Use MandateWallet when your agent holds its own key.
Sub-path Import
If you only need the client without the wallet (to avoid theviem dependency), use the sub-path export:
MandateClient and the error classes only. No viem, no signing utilities.
Next Steps
MandateWallet
High-level class that wraps MandateClient with local signing and broadcasting.
Error Classes
Full reference for PolicyBlockedError, CircuitBreakerError, and recovery patterns.
SDK Types
Complete TypeScript interfaces for all request and response objects.
Validate Transactions
Step-by-step guide for integrating validation into your agent loop.