# Agents and mandates

Source: https://docs.settlemint.com/docs/concepts/custody-and-signing/agent-mandates
An agent acts as its own participant under a mandate the chain enforces: allowed calls, spending ceilings, an expiry, and two kill switches the owner controls.

An agent is software that acts for an organisation on its own, without a person approving each step. A limit in the agent's own code, or in the scope of an API key, can be lifted by whoever holds that key. DALP puts the limit on chain, in a mandate: the list of calls the agent may make, how much of each asset it may move, and the date the grant ends. The organisation owner grants, changes and withdraws the mandate, and the agent's key cannot.

This page covers how the owner runs agents. [Agent mandate](https://docs.settlemint.com/docs/concepts/custody-and-signing/agent-mandate) covers what the mandate contract checks and the errors it reverts with.

## What an agent is [#what-an-agent-is]

An agent is a participant of kind `agent`, owned by one organisation. It has three parts:

- **A signer.** A managed custody key that the platform holds and signs the agent's user operations with. It is also the account's owner. The signature is a plain 65-byte ECDSA signature over the user operation hash.
- **An account.** A [Smart Wallet](https://docs.settlemint.com/docs/concepts/custody-and-signing/smart-wallet) created through the account factory, the contract that builds every account, with the mandate validator as its only validator. The mandate validator is the module that checks each user operation against the mandate.
- **A mandate owner.** The signing address of the organisation's operations account, a managed custody key the platform signs with when the organisation owner changes or revokes the mandate. Only this address can change or revoke the mandate.

The agent's id is its participant id, so the agent's activity is the participant activity that every [participant](https://docs.settlemint.com/docs/concepts/compliance-and-identity/participant) has. Everything the agent does is sent from its own account and signed by its own key. The platform never uses a person's key or API key to act for an agent.

Only the organisation owner can call the agent routes, reads included. A request from anyone else is refused with `DALP-199555`.

The DALP CLI and the local MCP server are a different path. They act as the person who signed in, with that person's role, as [using the CLI with AI agents](https://docs.settlemint.com/docs/api-reference/cli/ai-agents) describes. An agent that acts on its own does not use them.

## What a mandate holds [#what-a-mandate-holds]

A mandate is one record on the agent's account.

| Part | What it limits |
| --- | --- |
| Expiry | A unix timestamp after which every user operation is refused. It must lie in the future when the mandate is written. |
| Period | The length of the window, from 60 to 4,294,967,295 seconds, that the per-period ceilings count over. |
| Allowed targets | Up to 32 contracts, each with up to 16 function selectors the agent may call there. |
| Asset ceilings | Up to 16 ERC-20 assets, each with a ceiling per transaction and a ceiling per period. |
| Native ceilings | A ceiling per transaction and a ceiling per period for native value. A zero ceiling refuses every call that carries value. |

The period is a fixed number of seconds counted from the block that wrote the mandate. Each period starts a fresh count, and replacing a mandate starts a new count at once.

## What the chain refuses [#what-the-chain-refuses]

The mandate validator checks every user operation before the account executes it. A refusal reverts validation, so nothing runs. The platform's bundler finds this when it simulates the operation and does not submit it, so the refusal is recorded on the platform, not on chain. A signature that does not recover to the agent's key returns the standard ERC-4337 validation failure instead.

- **Calls the mandate does not list.** A call to a contract that is not on the list, or with a selector that contract's entry does not list, is refused.
- **Other account functions.** The only call data accepted is the account's `execute(bytes32,bytes)` entry point, as a single call or a batch. A delegate call is refused.
- **Calls to the account or the validator.** The agent's key cannot install or remove modules, change the account's settings, or change its own mandate.
- **Amounts over a ceiling.** The amount is read from the call itself. A metered asset can only be called with `transfer`, `transferFrom` or `approve`, and the per-transaction ceiling covers the whole user operation, so a batch cannot spend more than one call may. A user operation the validator refuses does not use up any of the period ceiling. Spend is recorded during validation, so one that passes validation and then reverts during execution still counts against both ceilings.
- **An expired or revoked mandate.** The user operation is refused before any call in it runs.

The platform runs the same check before it sends anything, and the chain enforces the mandate again, so a fault in the platform's check cannot let a call through. The ceilings count only the assets the mandate meters, so list only the calls the agent needs and meter every token it can move. The contract-level revert names are on [Agent mandate](https://docs.settlemint.com/docs/concepts/custody-and-signing/agent-mandate).

## Create an agent [#create-an-agent]

The owner creates an agent from Agents, under Organisation participants in the Console, or with `POST /api/v2/agents`. The request carries a name of 1 to 120 characters, the mandate and the owner's wallet verification, the confirmation the owner gives with their own wallet before a sensitive change.

The platform creates the agent's custody key, then its account through the account factory, with the mandate validator installed and the operations account's signing address as the mandate owner. The agent reads `provisioning` until that is done, then `active`. A set-up that cannot finish reads `failed`.

Creation is refused with `DALP-199554` when the mandate breaks one of the rules below, because the chain could not enforce it as written. The error's `data.rule` names the rule, and `data.target`, `data.asset` or `data.selector` names the entry that breaks it. Creation is also refused while the organisation kill switch is on, with `DALP-199552`.

| `data.rule` | What the mandate does | Entry named |
| --- | --- | --- |
| `expiry-not-future` | Its expiry is now or in the past. | None |
| `contract-repeated` | It lists the same contract twice. | `target` |
| `asset-repeated` | It gives the same asset two ceilings. | `asset` |
| `function-repeated` | It lists the same function twice on one contract. | `target`, `selector` |
| `asset-limit-missing` | It allows `transfer`, `transferFrom` or `approve` on a contract that has no ceiling. | `target`, `selector` |
| `asset-function-not-allowed` | It allows a function other than those three on an asset that has a ceiling. | `asset`, `selector` |

The Console checks the same rules while the owner edits the mandate. It names the offending entry by its short address and its function name or selector, and blocks submission until the entry is fixed. It also requires at least one contract and checks that addresses, selectors and ceilings are well formed. On a contract with an asset ceiling, the sheet offers only "Approve spending of an asset" and "Transfer an asset". If the server still refuses, for instance because the expiry passes while the sheet is open, the toast shows the same sentence built from `data.rule`.

### The starter mandate [#the-starter-mandate]

The Console fills the Create agent sheet from a starter mandate, which `GET /api/v2/agents/mandate-proposal` returns. The starter mandate allows:

- `transfer` of the asset each coupon or dividend payout on the organisation's own assets is paid in. The proposal names each asset with the payout feature it serves, `fixed-treasury-yield` for coupons or `dividend-yield` for dividends. A mandate cannot restrict recipients, so the ceilings are the only limit on where that asset goes. Assets beyond the mandate's 32 contracts or 16 metered assets are left out of the proposal.
- A per-day ceiling for each payout asset of the smaller of 1,000 whole units and 10% of the largest last payout in that asset. When no payout has run, the ceiling is 1,000 whole units. The native ceilings are zero.
- An expiry 30 days after the mandate is written.

The starter mandate is a draft. The owner reviews it, and can trim or extend the targets, selectors, per-asset ceilings and expiry before confirming with wallet verification. Reading the proposal creates nothing.

## Read an agent and its limits [#read-an-agent-and-its-limits]

Agents lists the organisation's agents with their name, status, account, creation time and revocation time. `GET /api/v2/agents` returns the same list.

An agent's page, and `GET /api/v2/agents/{agentId}`, shows the identity, account, signer and mandate owner. It also shows the mandate as the chain holds it: expiry, period, allowed targets and selectors, and ceilings. For each asset it shows what the agent has spent this period against the per-period ceiling. An entry without an asset is native value. The mandate is absent until it is on chain.

An agent has one of four statuses:

| Status | Meaning |
| --- | --- |
| `provisioning` | The key and account are being set up. |
| `active` | The mandate is on chain and the agent can act within it. |
| `revoked` | The mandate was revoked. The agent record shows when, and by which kill switch. |
| `failed` | The set-up did not finish. |

## Change a mandate [#change-a-mandate]

The owner replaces an active agent's whole mandate with Edit mandate in the Console, or with `PUT /api/v2/agents/{agentId}/mandate`. The request carries the new mandate and the owner's wallet verification. The replacement drops the previous allowed calls, asset rules and counters, and starts a new period count.

A replacement that breaks one of the mandate rules is refused with `DALP-199554`, the same way creation is. An agent that is not active is refused with `DALP-199553`, and any change while the organisation kill switch is on is refused with `DALP-199552`.

## Revoke one agent [#revoke-one-agent]

Revoke agent is the per-agent kill switch. The owner confirms in the Console, gives a reason of 1 to 500 characters and completes wallet verification. The API call is `POST /api/v2/agents/{agentId}/revocation`.

The platform marks the agent `revoked` at once and refuses its next action. It then submits the on-chain revocation, and once that transaction is mined the chain refuses every user operation before any call in it runs. The platform also tries to cancel the actions the agent queued in the last seven days. An action that already reached the network can still land, so read the agent's activity after a revoke. The agent reads `revoked`, with the revocation time and `agent-kill-switch` as the switch used. Other agents are not touched.

Revoking is final. A revoked mandate cannot be replaced or granted again on the same account, so the account keeps the evidence that the organisation withdrew the grant. To grant a new mandate, the owner creates a new agent. Any later mandate change on a revoked agent is refused with `DALP-199553`.

## Halt all agents [#halt-all-agents]

Halt all agents is the organisation kill switch. The owner gives a reason of 1 to 500 characters and completes wallet verification. `PUT /api/v2/agents/kill-switch` switches it on or off, and `GET /api/v2/agents/kill-switch` reads its state, the time of the last change and the reason.

Switching it on revokes every agent that is not already revoked, and a scheduled agent action that fires is refused with `organisation-halted`. Those agents read `revoked` with `organisation-kill-switch` as the switch used. While it is on, no agent can be created, changed or act, and creation is refused with `DALP-199552`.

Switching it off allows new agents. Agents that were already revoked stay revoked.

## How agent actions are checked and attributed [#how-agent-actions-are-checked-and-attributed]

Every feature that acts for an agent sends its calls through one execution step. A request names the agent, the calls, the origin and an idempotency key. The origin records the feature, a reference, whether the action was scheduled and, when a person asked, who.

The step has two forms. A check evaluates the request against the agent's state, the organisation kill switch and the mandate read from chain, and submits nothing. A run does the same check, writes an audit record attributed to the agent, and sends the calls as one user operation from the agent's account. A scheduled action runs the same way when it fires, so a revoked agent or a halted organisation refuses it. Scheduled payout funding and a [hosted MCP](https://docs.settlemint.com/docs/api-reference/cli/hosted-mcp) tool call with `execution` set to `mandate` both go through this step.

A refusal is `DALP-199551` with a reason. It is not retryable. Where the reason concerns a target, selector, asset, amount or limit, the refusal carries it.

| Reason | What stopped the action |
| --- | --- |
| `agent-not-active` | The agent is still being set up or its set-up failed. |
| `agent-revoked` | The agent's mandate was revoked. |
| `organisation-halted` | The organisation kill switch is on. |
| `mandate-missing` | No mandate is on chain for the agent. |
| `mandate-expired` | The mandate's expiry has passed. |
| `target-not-allowed` | The contract or selector is not on the mandate. |
| `self-call-refused` | The call targets the agent's account or the validator. |
| `asset-per-transaction-exceeded` | The amount is over the asset's per-transaction ceiling. |
| `asset-per-period-exceeded` | The amount is over what is left of the asset's period ceiling. |
| `native-per-transaction-exceeded` | The native value is over the per-transaction ceiling. |
| `native-per-period-exceeded` | The native value is over what is left of the period ceiling. |
| `amount-undecodable` | The amount cannot be read from the call data. |
| `chain-refused` | The chain refused a user operation the platform check allowed. |

A refusal means the mandate or a kill switch stopped the action by design, and the fix is a different action or a change by the owner.

## Errors [#errors]

| Code | When |
| --- | --- |
| `DALP-199551` | The mandate, the agent's state or a kill switch does not allow the action. The reason names which. |
| `DALP-199552` | The organisation kill switch is on. Ask the owner to switch it off. |
| `DALP-199553` | The agent is not active. Wait for set-up to finish, or create a new agent after a revoke. |
| `DALP-199554` | The mandate cannot be enforced as written. `data.rule` names the rule it breaks, and the entry that breaks it is named too. |
| `DALP-199555` | The caller is not the organisation owner. |

## Related pages [#related-pages]

Read [Agent mandate](https://docs.settlemint.com/docs/concepts/custody-and-signing/agent-mandate) for the contract checks and revert names, [Smart Wallet](https://docs.settlemint.com/docs/concepts/custody-and-signing/smart-wallet) for the account an agent runs on, [User operations](https://docs.settlemint.com/docs/concepts/custody-and-signing/user-operations) for how its signed instructions reach the chain, and [Participant](https://docs.settlemint.com/docs/concepts/compliance-and-identity/participant) for the record that carries the agent's id and activity.
