> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://www.openfort.io/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

# Agentic wallets

Give an autonomous agent an on-chain wallet it fully controls, then constrain it with [signing policies](https://www.openfort.io/docs/configuration/policies) so it can only touch the contracts, methods, and amounts you permit. The agent acts on its own schedule; the policy is the guardrail.

Use this for trading bots, treasury automation, on-chain data agents, or any long-running process that needs to transact without a human in the loop.

## Prerequisites

Complete the [backend setup](https://www.openfort.io/docs/products/server/setup), then initialize the SDK:

```ts [openfort.ts]
import Openfort from '@openfort/openfort-node'

export const openfort = new Openfort(process.env.OPENFORT_SECRET_KEY!, {
  walletSecret: process.env.OPENFORT_WALLET_SECRET!,
})
```

:::info
Every `openfort.accounts.evm.backend.*` call needs your **wallet secret** — without it, wallet creation and signing fail. Grab it from the dashboard (see [backend setup](https://www.openfort.io/docs/products/server/setup)) and keep it in the environment.
:::

::::steps
## Create the agent's wallet

```ts
import { openfort } from './openfort'

const agentWallet = await openfort.accounts.evm.backend.create()
// Persist agentWallet.id — this is the agent's identity on-chain.
```

## Bound it with a signing policy

The guardrail. A [signing policy](https://www.openfort.io/docs/configuration/policies) is account- or project-scoped, and Openfort enforces it **automatically** on every operation the wallet signs — it's also what `policies_evaluate` pre-flights. This is *not* the same as the gas sponsorship in the next step.

A backend wallet transacts via EIP-7702, which signs several operations under the hood, so the policy must accept **all of them** or the send fails with `Forbidden`. Allow the transaction operations only for the contracts the agent needs, and allow the auxiliary signatures the flow requires:

```ts
const txOps = ['signEvmTransaction', 'sendEvmTransaction', 'sponsorEvmTransaction']

const guardrail = await openfort.policies.create({
  scope: 'account',
  accountId: agentWallet.id,
  rules: [
    // Allow transactions only to the contracts you approve — reject everything else.
    ...txOps.flatMap((operation) => [
      {
        action: 'accept',
        operation,
        criteria: [
          { type: 'evmNetwork', operator: 'in', chainIds: [84532] },
          { type: 'evmAddress', operator: 'in', addresses: ['{{CONTRACT_ADDRESS}}'] },
        ],
      },
      { action: 'reject', operation },
    ]),
    // Allow the auxiliary signatures a 7702 send needs.
    { action: 'accept', operation: 'signEvmHash' },
    { action: 'accept', operation: 'signEvmTypedData' },
    { action: 'accept', operation: 'signEvmMessage' },
  ],
})
```

Openfort is **fail-closed**: if policies exist but no rule matches, the operation is rejected. Anything outside your allowlist is denied before it reaches the chain.

:::warning
**Keep the guardrail and the gas sponsorship in separate policies.** Linking a signing policy to a fee sponsorship (next step) converts it into a sponsorship policy — it stops acting as a guardrail, and `policies_evaluate` will start returning *allowed* for everything. Use one policy for the guardrail and a different one for gas.
:::

## Sponsor gas

Backend wallets hold no ETH, so a transaction needs a fee sponsorship — otherwise the intent is **created but never broadcast** and stays pending indefinitely, with no error. Create a separate sponsorship policy and a `pay_for_user` fee sponsorship:

```ts
const gasPolicy = await openfort.policies.create({
  scope: 'project',
  rules: [
    {
      action: 'accept',
      operation: 'sponsorEvmTransaction',
      criteria: [{ type: 'evmNetwork', operator: 'in', chainIds: [84532] }],
    },
  ],
})

const sponsorship = await openfort.feeSponsorship.create({
  strategy: { sponsorSchema: 'pay_for_user' }, // you sponsor the gas
  policyId: gasPolicy.id,
})
```

## Send and confirm

Pass the sponsorship on every transaction. The guardrail policy is enforced automatically; the sponsorship pays the gas.

```ts
const result = await openfort.accounts.evm.backend.sendTransaction({
  account: agentWallet,
  chainId: 84532,
  interactions: [{ to: '{{CONTRACT_ADDRESS}}', data: '{{CALLDATA}}' }],
  policy: sponsorship.id, // gas sponsorship — the guardrail is enforced automatically
})

// The on-chain hash is not available synchronously — poll the intent until it's mined:
let hash: string | undefined
while (!hash) {
  const intent = await openfort.transactionIntents.get(result.id)
  hash = intent.response?.transactionHash
  if (!hash) await new Promise((resolve) => setTimeout(resolve, 1500))
}
```

If a call falls outside the guardrail, Openfort rejects it before it reaches the chain — so a misbehaving or compromised agent can't drain funds or hit contracts you never approved.
::::

## Connect an agent suite

Any MCP-capable agent framework can drive an Openfort wallet through the [CLI MCP server](https://www.openfort.io/docs/overview/building-with-ai#cli-mcp-server) — every CLI command becomes a tool, so the agent can create wallets, pre-flight policies, and submit transactions itself. Give it a policy-bounded wallet, point the suite at the server, and scope it to the tools it needs:

* `accounts_evm_create` — create its wallet
* `policies_evaluate` — pre-flight check an action against a policy
* `transactions_create` — submit a transaction

The server definition is the same everywhere — a stdio MCP server run through the CLI:

```json
{
  "mcpServers": {
    "openfort": {
      "command": "npx",
      "args": ["@openfort/cli", "--mcp"],
      "env": { "OPENFORT_API_KEY": "${OPENFORT_API_KEY}" }
    }
  }
}
```

:::warning
The CLI MCP server acts with your secret key (the CLI reads it from `OPENFORT_API_KEY`). Keep it in the environment — never inline in a committed config — and pair every agent wallet with a [policy](https://www.openfort.io/docs/configuration/policies) so no tool call can exceed what you allow.
:::

### OpenClaw

Add the server under `mcpServers` in `~/.openclaw/openclaw.json` (or run `openclaw mcp add`), then reload the gateway:

```json [~/.openclaw/openclaw.json]
{
  "mcpServers": {
    "openfort": {
      "command": "npx",
      "args": ["@openfort/cli", "--mcp"],
      "env": { "OPENFORT_API_KEY": "${OPENFORT_API_KEY}" }
    }
  }
}
```

To hand the wallet tools to a single agent rather than all of them, list `openfort` under that agent with OpenClaw's per-agent routing. See OpenClaw's [MCP documentation](https://docs.openclaw.ai/cli/mcp).

### Hermes

Add the server under `mcp_servers` in `~/.hermes/config.yaml` (or run `hermes mcp add`), then start Hermes or `/reload-mcp`. Use a `tools` allowlist so the agent only sees the wallet tools it needs:

```yaml [~/.hermes/config.yaml]
mcp_servers:
  openfort:
    command: "npx"
    args: ["@openfort/cli", "--mcp"]
    env:
      OPENFORT_API_KEY: "${OPENFORT_API_KEY}"
    tools:
      include: [accounts_evm_create, policies_evaluate, transactions_create]
```

See the Hermes [MCP guide](https://hermes-agent.nousresearch.com/docs/guides/use-mcp-with-hermes).

## Guardrails

* **Two policies, not one.** A signing policy is the guardrail (enforced automatically, checked by `policies_evaluate`); a fee sponsorship pays gas. Don't reuse one for the other.
* **Cover every operation.** A backend send needs `signEvmTransaction`, `sendEvmTransaction`, `sponsorEvmTransaction`, and the `signEvmHash` / `signEvmTypedData` / `signEvmMessage` operations — a policy that allows only `signEvmTransaction` fails the send with `Forbidden`.
* **Pre-flight with `policies_evaluate`** so the agent checks whether an action is allowed before spending gas.
* **One wallet per agent** so you can audit, rate-limit, and revoke each independently.
* **Watch it** with [webhooks](https://www.openfort.io/docs/configuration/webhooks) to alert on every transaction the agent submits.

## Solana

This guide uses EVM backend wallets. The same shape works for Solana — a policy-bounded wallet the agent drives — with a few differences:

* **Use `openfort.accounts.solana.backend`** and a `cluster` (e.g. `devnet`) instead of `chainId`.
* **Gas is handled by Kora**, a co-signer / fee payer, not a fee sponsorship — there's no `policy` param for gas, the send is gasless through Kora. It needs `@solana/kit`, `@solana/kora`, `@solana-program/compute-budget`, and `@solana/transaction-confirmation` installed.
* **The policy uses Solana operations and criteria.** Allowlist the programs and accounts the agent may touch with `programId`, `solAddress`, `splAddress`, and `mintAddress`; the operations are `signSolTransaction`, `sendSolTransaction`, `sponsorSolTransaction`, and `signSolMessage`. There are no EIP-7702 auxiliary signatures, so there's no extra operation to allow and no `Forbidden` from a missing one.

`sendTransaction` confirms on-chain and returns the transaction `signature` directly — no polling:

```ts
const result = await openfort.accounts.solana.backend.sendTransaction({
  account: agentWallet,
  cluster: 'devnet',
  instructions: [...myInstructions],
})
// result.signature
```

## Next steps

* [Signing policies](https://www.openfort.io/docs/configuration/policies) — the full policy model.
* [Agent permissions recipe](https://www.openfort.io/docs/recipes/agent-permissions) — a worked example.
* [User + server signers](https://www.openfort.io/docs/products/server/workflows/user-and-server-signers) — for an agent that acts on behalf of a specific user.
