Autonomous transactions let an agent execute on-chain MCP tool results (transfers, swaps, identity registration txs) without manual confirmation when your integrator is configured to sign.
Use this when you trust the agent’s interaction policies and want external MCP clients (Cursor, Claude, CLI) to queue transactions for your backend instead of returning unsigned payloads only.
Enabling autonomous transactions increases risk. Combine with strict interaction policies, monitoring, and webhook signing secrets. Operators see an Autonomous indicator in the dashboard All agents table.
Two execution paths
| Context | MCP header | MCP response | Who signs |
|---|
| In-app chat (managed) | X-Agent-Kit-Execution-Context: managed | unsigned_transaction_ready | Your app backend (immediate) |
| External MCP (delegated) | omitted or delegated | transaction_pending when autonomous ON | Your integrator backend |
When autonomous is OFF and the caller is external, Kit returns unsigned_transaction_ready only — same as before.
Enable per agent
Dashboard (sample app)
In the sample web app (Agents → Configuration), toggle Autonomous transactions.
The Abstraxn developer dashboard (Agentic Stack → All agents) shows an Autonomous column and warning in the agent detail panel when enabled.
SDK / API
import { AgentKitClient } from '@abstraxn/agent-kit';
const kit = new AgentKitClient({ apiKey: process.env.AGENT_KIT_API_KEY! });
await kit.updateAutonomousTx(agentId, {
enabled: true,
maxPendingAgeSeconds: 300,
maxConcurrentPending: 10,
});
REST equivalent: PATCH /agents/:agentId/autonomous-tx
Config is stored in agent.metadata.autonomousTx on Agent Kit.
Integrator execution loop (delegated)
Any backend that holds the agent accessKey can execute queued transactions. You need two pieces: (1) register where Kit should send events, and (2) run a receiver that verifies signatures and signs txs.
Register the webhook (where Kit sends events)
Not configured inside agent-app-service. Registration creates a row in Agent Kit’s webhook_subscriptions table.
Dashboard (recommended)
SDK / API
- Open Agentic Stack → Settings → Webhooks in the Abstraxn Dashboard.
- Click Add webhook.
- Set URL to your integrator endpoint, e.g.
https://api.yourapp.com/kit-webhooks/transactions.
- Set or copy the HMAC secret — use the same value as
KIT_WEBHOOK_SECRET on your backend.
- Select events (at minimum
transaction.created).
- Optionally filter to specific agent IDs; leave empty for all agents.
You can list, edit, or remove subscriptions from the same page.await kit.createWebhookSubscription({
url: 'https://api.yourapp.com/kit-webhooks/transactions',
secret: process.env.KIT_WEBHOOK_SECRET!,
events: ['transaction.created', 'transaction.updated', 'transaction.expired'],
});
REST: POST /webhooks with dashboard X-API-Key.
Receive webhooks (sample app: agent-app-service)
The sample backend does not register webhooks — it only listens:
| Item | Value |
|---|
| Endpoint | POST /kit-webhooks/transactions |
| Env | KIT_WEBHOOK_SECRET (must match subscription secret) |
| Handler | Verifies HMAC → on transaction.created → claim → sign → broadcast → PATCH Kit status |
AGENT_KIT_API_KEY= # integrator dashboard API key
AGENT_KIT_BASE_URL= # Agent Kit base URL
KIT_WEBHOOK_SECRET= # same as dashboard webhook secret
KIT_PENDING_TX_POLL_INTERVAL_MS=30000 # poll fallback (0 = disable)
Kit POSTs signed JSON when a pending transaction is created or updated. Retries up to 3 times (0s, 2s, 8s) until your endpoint returns HTTP 2xx.
On transaction.created — claim, sign, broadcast
const pending = await kit.claimPendingTransaction(agentId, pendingTransactionId);
// Sign pending.mcpResult with accessKey + @abstraxn/server-signer
await kit.updatePendingTransaction(agentId, pendingTransactionId, {
status: 'confirmed',
txHash: '0x...',
});
Poll fallback
If webhooks are unreachable, poll GET /agents/:agentId/transactions/pending?status=pending. The sample app does this every KIT_PENDING_TX_POLL_INTERVAL_MS (default 30s).
Webhook verification
Kit sends:
X-Agent-Kit-Event
X-Agent-Kit-Timestamp
X-Agent-Kit-Signature: sha256=<hmac>
Signature: HMAC-SHA256(secret, timestamp + "." + rawBody)
| Header | Values | Default |
|---|
X-Agent-Kit-Execution-Context | managed | delegated | delegated |
X-Agent-Kit-Idempotency-Key | optional string | — |
In-app chat should always send managed. Cursor / Claude MCP clients default to delegated.
const mcp = kit.createMcpClient(agent.apiKey, {
executionContext: 'delegated', // default for external tools
});
await mcp.callTool('transfer', { chain: 'base', to: '0x...', amount: '1' }, {
executionContext: 'delegated',
});
Transaction creation response
When autonomous is enabled and the caller is delegated, MCP returns:
{
"status": "transaction_pending",
"pendingTransactionId": "uuid",
"expiresAt": "2026-06-29T12:00:00.000Z",
"intent": { },
"transaction": { },
"warnings": [
{
"code": "autonomous_tx_enabled",
"severity": "high",
"message": "This agent has autonomous transactions enabled..."
}
]
}
Your integrator should treat warnings as operator-facing risk signals.
All MCP tools that produce signable transactions:
transfer
uniswap_swap_quote, lifi_swap_quote, jupiter_swap_quote, oneinch_swap_quote, evm_swap_quote
- ERC-8004 identity prepare flows (REST)
Read-only tools are unchanged.
Checks and limits
| Check | When |
|---|
autonomousTx.enabled | Before queueing delegated txs |
| Interaction policies | MCP tools/call (existing) |
Agent isActive | Queue create + claim |
maxConcurrentPending | Per agent (default 10) |
| Pending TTL | Default 300s; expired txs → transaction.expired webhook |
| Idempotency key | Optional header dedupes queue rows |
Sample app wiring
The sample backend (agent-app-service):
- Proxies
GET/PATCH /agents/:id/autonomous-tx
- Sends
managed on chat MCP calls
- Polls Kit pending txs (
KIT_PENDING_TX_POLL_INTERVAL_MS)
- Exposes
POST /kit-webhooks/transactions to receive Kit webhooks (registration is via dashboard or POST /webhooks on Kit — not in agent-app env)
Delegated executions appear in the sample app Activities view with an Autonomous tag, status, and receipt when TRANSFER_WAIT_FOR_RECEIPT=true.