Intents API
The Intents API lets an agent submit on-chain transactions — transfers, swaps, contract calls — while never having access to the raw private key. The server signs the transaction using keys stored in the vault and broadcasts it through a dedicated RPC for the target chain.
On this page
- Quickstart
- How it works
- Submitting a transaction
- Sign-only mode
- Transaction simulation
- Multi-chain signing keys
- Non-EVM signing
- Unified sign endpoint
- Transaction guardrails
- Execution Intents
- Best practices
- Next steps
Try out the examples: Transaction Simulation (guardrails + Tenderly simulation), Shroud Demo (Intents API via Shroud TEE), Multi-Chain Keys (provision keys for 6 blockchains), EVM Signing (EIP-191, EIP-712, tx types 0–2), and Agentic TX (real mainnet transactions with guardrails).
Quickstart: Your first transaction (~5 min)
- Create an agent with
intents_api_enabled: true(Dashboard → Agents → Create, or API below). Note the agent ID and API key. - Store a signing key in a vault the agent can read: either provision a per-chain signing key via
POST /v1/agents/:id/signing-keys(recommended), or put a secp256k1 private key at a path likekeys/ethereum-signerorwallets/hot-wallet(see Secrets). Grant the agent read access to that path via a policy. - Get an agent JWT:
POST /v1/auth/agent-tokenwithagent_idandapi_key. - Submit a transaction:
POST /v1/agents/:agent_id/transactionswithchain,to,value, and optionallysigning_key_path. Use testnets (e.g.chain: "sepolia") first. - Optional: Set
simulate_first: trueto run a Tenderly simulation before signing; if the simulation reverts, the API returns 422 and does not sign. See Transaction simulation (Tenderly) and Error codes.
Default signing key path auto-resolves: if the agent has a per-chain signing key provisioned (via POST /v1/agents/:id/signing-keys), the key at agents/{id}/chains/{chain}/private_key is used; otherwise falls back to keys/{chain}-signer (e.g. keys/base-signer). Network names like sepolia and base automatically map to canonical signing key chains like ethereum. You can override with signing_key_path in the request. Allowed path prefixes: keys/, wallets/, agents/{id}/keys/, agents/{id}/chains/.
How it works
Agent 1claw Vault Blockchain
│ │ │
│ POST /v1/agents/:id/ │ │
│ transactions │ │
│ { chain, to, value, │ │
│ data, signing_key_path } │ │
│ ─────────────────────────► │ │
│ │ 1. Decrypt private key │
│ │ from vault via HSM │
│ │ 2. Build & sign tx │
│ │ 3. Broadcast via RPC ───► │
│ │ │
│ ◄───────────────────────── │ tx_hash, status │
│ { id, tx_hash, status } │ │
- The agent calls
POST /v1/agents/:agent_id/transactionswith the chain, recipient, value, calldata, and the vault path to the signing key. - The vault decrypts the private key inside the HSM boundary, constructs and signs the transaction, and broadcasts it to the chain's RPC endpoint.
- The agent receives an
idandtx_hash— it never sees the raw key material.
Enabling the Intents API
Set intents_api_enabled: true when registering or updating an agent:
- curl
- TypeScript
- Python
curl -X POST "https://api.1claw.xyz/v1/agents" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "DeFi Bot",
"intents_api_enabled": true
}'
import { createClient } from "@1claw/sdk";
const client = createClient({
baseUrl: "https://api.1claw.xyz",
apiKey: process.env.ONECLAW_API_KEY,
});
const { data } = await client.agents.create({
name: "DeFi Bot",
intents_api_enabled: true,
});
from oneclaw import create_client
client = create_client(api_key="1ck_...")
resp = client.agents.create(
"DeFi Bot",
intents_api_enabled=True,
)
agent_id = resp.data["agent"]["id"]
What changes when enabled
| Behaviour | intents_api_enabled: false | intents_api_enabled: true |
|---|---|---|
Read api_key, password, etc. | Allowed | Allowed |
Read private_key or ssh_key | Allowed | Blocked (403) |
| Submit proxy transactions | Not available | Allowed |
| Audit trail per transaction | N/A | Full trace with tx_id |
The enforcement is two-sided: the flag both grants access to the transaction endpoints and blocks direct reads of signing keys through the standard secrets endpoint. This guarantees the agent can only use keys through the proxy.
Submitting a transaction
- curl
- TypeScript
- Python
curl -X POST "https://api.1claw.xyz/v1/agents/$AGENT_ID/transactions" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"chain": "ethereum",
"to": "0xRecipientAddress",
"value": "1.0",
"data": "0x",
"signing_key_path": "wallets/hot-wallet"
}'
const { data: tx } = await client.agents.submitTransaction(agentId, {
chain: "ethereum",
to: "0xRecipientAddress",
value: "1.0",
data: "0x",
signing_key_path: "wallets/hot-wallet",
});
from oneclaw import create_client
client = create_client(api_key="ocv_...")
resp = client.agents.submit_transaction(
agent_id,
chain="ethereum",
to="0xRecipientAddress",
value="1.0",
data="0x",
signing_key_path="wallets/hot-wallet",
)
print(resp.data.get("tx_hash"), resp.data.get("status"))
Response
{
"id": "a7e2c...",
"tx_hash": "0xabc123...",
"chain": "ethereum",
"status": "broadcast"
}
Querying transactions
- curl
- TypeScript
- Python
# List all transactions for this agent
curl "https://api.1claw.xyz/v1/agents/$AGENT_ID/transactions" \
-H "Authorization: Bearer $AGENT_TOKEN"
# Get a specific transaction
curl "https://api.1claw.xyz/v1/agents/$AGENT_ID/transactions/$TX_ID" \
-H "Authorization: Bearer $AGENT_TOKEN"
// List transactions
const { data: txList } = await client.agents.listTransactions(agentId);
// Get transaction
const { data: tx } = await client.agents.getTransaction(agentId, txId);
from oneclaw import create_client
client = create_client(api_key="1ck_...")
agents = client.agents.list()
for a in agents.data["agents"]:
print(a["name"], a["id"])
Sign-only mode (BYORPC)
Sometimes you want the server to sign the transaction inside the HSM (or Shroud TEE) but not broadcast it. This lets you:
- Use your own RPC endpoint for broadcasting
- Implement MEV protection (e.g. Flashbots, MEV Blocker)
- Queue transactions for batch submission
- Broadcast to multiple RPCs simultaneously
Call POST /v1/agents/:agent_id/transactions/sign with the same request body as submit. The server signs the transaction and returns the raw signed_tx hex without broadcasting.
- curl
- TypeScript
- Python
- CLI
curl -X POST "https://api.1claw.xyz/v1/agents/$AGENT_ID/transactions/sign" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"chain": "ethereum",
"to": "0xRecipientAddress",
"value": "0.1",
"signing_key_path": "keys/ethereum-signer"
}'
const { data: signedTx } = await client.agents.signTransaction(agentId, {
chain: "ethereum",
to: "0xRecipientAddress",
value: "0.1",
signing_key_path: "keys/ethereum-signer",
});
// Broadcast yourself using ethers, viem, or raw RPC
console.log(signedTx.signed_tx); // 0x02f8...
console.log(signedTx.tx_hash); // 0xabc123...
from oneclaw import create_client
client = create_client(api_key="1ck_...")
resp = client.agents.create(
"my-agent",
description="CI/CD bot",
intents_api_enabled=True,
)
agent = resp.data["agent"]
api_key = resp.data.get("api_key") # shown once
1claw agent tx sign $AGENT_ID \
--to 0xRecipientAddress \
--value 0.1 \
--chain ethereum
Response
{
"signed_tx": "0x02f870018203...signed hex...",
"tx_hash": "0xabc123...",
"from": "0xDerivedSenderAddress",
"to": "0xRecipientAddress",
"chain": "ethereum",
"chain_id": 1,
"nonce": 42,
"value_wei": "100000000000000000",
"status": "sign_only"
}
All agent guardrails (allowlists, value caps, daily limits) are enforced exactly as for submit. The transaction is recorded for audit and daily-limit tracking.
When using Shroud (shroud.1claw.xyz), the /transactions/sign endpoint performs signing inside the TEE — the private key never leaves the secure enclave, and you get full control over broadcasting.
Transaction simulation (Tenderly)
Every transaction can be simulated before signing. Simulation executes the full transaction against the current chain state in a sandboxed environment, returning decoded traces, balance changes, gas estimates, and human-readable error messages — without consuming real gas.
Standalone simulation
Call the simulate endpoint to preview a transaction without committing:
- curl
- TypeScript
- Python
curl -X POST "https://api.1claw.xyz/v1/agents/$AGENT_ID/transactions/simulate" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"chain": "base",
"to": "0xRecipientAddress",
"value": "0.5",
"data": "0x",
"signing_key_path": "wallets/hot-wallet"
}'
const { data: sim } = await client.agents.simulateTransaction(agentId, {
chain: "base",
to: "0xRecipientAddress",
value: "0.5",
data: "0x",
signing_key_path: "wallets/hot-wallet",
});
from oneclaw import create_client
client = create_client(api_key="1ck_...")
resp = client.agents.create(
"my-agent",
description="CI/CD bot",
intents_api_enabled=True,
)
agent = resp.data["agent"]
api_key = resp.data.get("api_key") # shown once
The response includes:
{
"simulation_id": "sim_a7e2c...",
"status": "success",
"gas_used": 21000,
"balance_changes": [
{ "address": "0xSender...", "token": "ETH", "before": "2.5", "after": "1.99", "change": "-0.51" },
{ "address": "0xRecipient...", "token": "ETH", "before": "0.0", "after": "0.5", "change": "+0.5" }
],
"tenderly_dashboard_url": "https://dashboard.tenderly.co/..."
}
Simulate-then-sign (single call)
Add "simulate_first": true to the standard transaction submission. The server simulates first; if the simulation reverts, it returns HTTP 422 and does not sign or broadcast:
- curl
- TypeScript
- Python
curl -X POST "https://api.1claw.xyz/v1/agents/$AGENT_ID/transactions" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"chain": "base",
"to": "0xRecipientAddress",
"value": "0.5",
"simulate_first": true
}'
const { data: tx } = await client.agents.submitTransaction(agentId, {
chain: "base",
to: "0xRecipientAddress",
value: "0.5",
simulate_first: true,
});
from oneclaw import create_client
client = create_client(api_key="ocv_...")
resp = client.agents.submit_transaction(
agent_id,
chain="ethereum",
to="0x000000000000000000000000000000000000dEaD",
value="0",
)
print(resp.data.get("tx_hash"))
Bundle simulation
Simulate multiple transactions sequentially (e.g. ERC-20 approve followed by a swap):
- curl
- TypeScript
- Python
curl -X POST "https://api.1claw.xyz/v1/agents/$AGENT_ID/transactions/simulate-bundle" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"transactions": [
{ "chain": "base", "to": "0xToken", "value": "0", "data": "0xapprove..." },
{ "chain": "base", "to": "0xRouter", "value": "0", "data": "0xswap..." }
]
}'
const { data: bundle } = await client.agents.simulateBundle(agentId, {
transactions: [
{ chain: "base", to: "0xToken", value: "0", data: "0xapprove..." },
{ chain: "base", to: "0xRouter", value: "0", data: "0xswap..." },
],
});
from oneclaw import create_client
client = create_client(api_key="1ck_...")
resp = client.agents.create(
"my-agent",
description="CI/CD bot",
intents_api_enabled=True,
)
agent = resp.data["agent"]
api_key = resp.data.get("api_key") # shown once
Enforcing simulation
Org admins can require simulation for all agent transactions by setting the intents_api.require_simulation org setting to "true" via PUT /v1/admin/settings/intents_api.require_simulation. When enabled, any transaction submitted without simulate_first: true will be automatically simulated, and reverts will block signing.
EIP-1559 (Type 2) transactions
Set max_fee_per_gas and max_priority_fee_per_gas instead of gas_price to use EIP-1559 fee mode:
- curl
- TypeScript
- Python
curl -X POST "https://api.1claw.xyz/v1/agents/$AGENT_ID/transactions" \
-H "Authorization: Bearer $AGENT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"chain": "base",
"to": "0xRecipientAddress",
"value": "0.1",
"max_fee_per_gas": "30000000000",
"max_priority_fee_per_gas": "1500000000",
"simulate_first": true
}'
const { data: tx } = await client.agents.submitTransaction(agentId, {
chain: "base",
to: "0xRecipientAddress",
value: "0.1",
max_fee_per_gas: "30000000000",
max_priority_fee_per_gas: "1500000000",
simulate_first: true,
});
from oneclaw import create_client
client = create_client(api_key="ocv_...")
resp = client.agents.submit_transaction(
agent_id,
chain="ethereum",
to="0x000000000000000000000000000000000000dEaD",
value="0",
)
print(resp.data.get("tx_hash"))
Split guides
- Signing & chains — multi-chain keys, non-EVM, unified sign, MCP tools, supported chains
- Guardrails & security — transaction guardrails, TEE signing, Execution Intents, best practices
Next steps
- Multi-chain signing keys — provision per-chain keypairs for agents
- Shroud TEE signing — route signing through the confidential enclave
- Treasury — Safe multisigs and delegated agent signing
- Transaction guardrails — per-agent spend caps and allowlists
- Error codes — Intents API error reference