Skip to main content
End-to-end curl workflows for common ISCL operations. Each recipe shows the complete sequence of API calls from intent to confirmation.
Base URL: http://localhost:3100Prerequisites: ISCL Core running with at least one wallet imported and RPC configured.

Recipe 1: ERC-20 Token Transfer (Full Pipeline)

Transfer 100 USDC on Base chain.
1

Check the sender's balance

curl -s http://localhost:3100/v1/balance/0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913/0xYourWalletAddress?chainId=8453 | jq
2

Request approval (build + preflight + approve)

curl -s -X POST http://localhost:3100/v1/tx/approve-request \
  -H "Content-Type: application/json" \
  -d '{
    "version": "1",
    "id": "550e8400-e29b-41d4-a716-446655440001",
    "timestamp": 1700000000000,
    "chain": { "type": "evm", "chainId": 8453 },
    "wallet": { "address": "0xYourWalletAddress" },
    "action": {
      "type": "transfer",
      "asset": {
        "kind": "erc20",
        "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
        "symbol": "USDC",
        "decimals": 6
      },
      "to": "0xRecipientAddress",
      "amount": "100000000"
    },
    "constraints": {
      "maxGasWei": "1000000000000000",
      "deadline": 1700003600,
      "maxSlippageBps": 0
    },
    "metadata": { "source": "curl-cookbook", "note": "Send 100 USDC" }
  }' | jq
3

Sign and broadcast

curl -s -X POST http://localhost:3100/v1/tx/sign-and-send \
  -H "Content-Type: application/json" \
  -d '{
    "intent": {
      "version": "1",
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "timestamp": 1700000000000,
      "chain": { "type": "evm", "chainId": 8453 },
      "wallet": { "address": "0xYourWalletAddress" },
      "action": {
        "type": "transfer",
        "asset": {
          "kind": "erc20",
          "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
          "symbol": "USDC",
          "decimals": 6
        },
        "to": "0xRecipientAddress",
        "amount": "100000000"
      },
      "constraints": {
        "maxGasWei": "1000000000000000",
        "deadline": 1700003600,
        "maxSlippageBps": 0
      },
      "metadata": { "source": "curl-cookbook", "note": "Send 100 USDC" }
    },
    "approvalTokenId": "tok_abc123..."
  }' | jq
4

Check the transaction receipt

curl -s http://localhost:3100/v1/tx/0xdef456... | jq

Recipe 2: Native ETH Transfer

Send 0.01 ETH on Base. This script extracts the approval token automatically and pipes it into the sign-and-send call.
1

Approve the transfer

RESPONSE=$(curl -s -X POST http://localhost:3100/v1/tx/approve-request \
  -H "Content-Type: application/json" \
  -d '{
    "version": "1",
    "id": "660e8400-e29b-41d4-a716-446655440002",
    "timestamp": 1700000000000,
    "chain": { "type": "evm", "chainId": 8453 },
    "wallet": { "address": "0xYourWalletAddress" },
    "action": {
      "type": "transfer_native",
      "to": "0xRecipientAddress",
      "amount": "10000000000000000"
    },
    "constraints": {
      "maxGasWei": "1000000000000000",
      "deadline": 1700003600,
      "maxSlippageBps": 0
    },
    "metadata": { "source": "curl-cookbook" }
  }')

echo "$RESPONSE" | jq

# Extract approval token
TOKEN=$(echo "$RESPONSE" | jq -r '.approvalTokenId')
2

Sign and send using the extracted token

curl -s -X POST http://localhost:3100/v1/tx/sign-and-send \
  -H "Content-Type: application/json" \
  -d "{
    \"intent\": {
      \"version\": \"1\",
      \"id\": \"660e8400-e29b-41d4-a716-446655440002\",
      \"timestamp\": 1700000000000,
      \"chain\": { \"type\": \"evm\", \"chainId\": 8453 },
      \"wallet\": { \"address\": \"0xYourWalletAddress\" },
      \"action\": {
        \"type\": \"transfer_native\",
        \"to\": \"0xRecipientAddress\",
        \"amount\": \"10000000000000000\"
      },
      \"constraints\": {
        \"maxGasWei\": \"1000000000000000\",
        \"deadline\": 1700003600,
        \"maxSlippageBps\": 0
      },
      \"metadata\": { \"source\": \"curl-cookbook\" }
    },
    \"approvalTokenId\": \"$TOKEN\"
  }" | jq
The transfer_native action type does not require an asset field — it always sends the chain’s native currency (ETH on Ethereum, Optimism, Arbitrum, and Base).

Recipe 3: DEX Swap (Uniswap V3)

Swap 0.1 WETH for USDC on Base using the Uniswap V3 router.
1

Submit the swap intent for approval

curl -s -X POST http://localhost:3100/v1/tx/approve-request \
  -H "Content-Type: application/json" \
  -d '{
    "version": "1",
    "id": "770e8400-e29b-41d4-a716-446655440003",
    "timestamp": 1700000000000,
    "chain": { "type": "evm", "chainId": 8453 },
    "wallet": { "address": "0xYourWalletAddress" },
    "action": {
      "type": "swap_exact_in",
      "router": "0x2626664c2603336E57B271c5C0b26F421741e481",
      "assetIn": {
        "kind": "erc20",
        "address": "0x4200000000000000000000000000000000000006",
        "symbol": "WETH",
        "decimals": 18
      },
      "assetOut": {
        "kind": "erc20",
        "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
        "symbol": "USDC",
        "decimals": 6
      },
      "amountIn": "100000000000000000",
      "minAmountOut": "250000000"
    },
    "constraints": {
      "maxGasWei": "2000000000000000",
      "deadline": 1700003600,
      "maxSlippageBps": 100
    },
    "metadata": { "source": "curl-cookbook", "note": "Swap 0.1 WETH for USDC" }
  }' | jq
2

Sign and send

Call POST /v1/tx/sign-and-send with the full intent and the returned approvalTokenId, following the same pattern as Recipe 1 Step 3.
The Uniswap V3 router address varies by chain:
  • Base (8453): 0x2626664c2603336E57B271c5C0b26F421741e481
  • Ethereum, Optimism, Arbitrum: 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45

Recipe 4: DEX Swap via 1inch Aggregator

Same swap as Recipe 3, but routed through 1inch for potentially better pricing across multiple DEXs. The only differences are adding "provider": "1inch" and using the 1inch router address.
curl -s -X POST http://localhost:3100/v1/tx/approve-request \
  -H "Content-Type: application/json" \
  -d '{
    "version": "1",
    "id": "880e8400-e29b-41d4-a716-446655440004",
    "timestamp": 1700000000000,
    "chain": { "type": "evm", "chainId": 8453 },
    "wallet": { "address": "0xYourWalletAddress" },
    "action": {
      "type": "swap_exact_in",
      "router": "0x111111125421cA6dc452d289314280a0f8842A65",
      "provider": "1inch",
      "assetIn": {
        "kind": "erc20",
        "address": "0x4200000000000000000000000000000000000006",
        "symbol": "WETH",
        "decimals": 18
      },
      "assetOut": {
        "kind": "erc20",
        "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
        "symbol": "USDC",
        "decimals": 6
      },
      "amountIn": "100000000000000000",
      "minAmountOut": "250000000"
    },
    "constraints": {
      "maxGasWei": "2000000000000000",
      "deadline": 1700003600,
      "maxSlippageBps": 100
    },
    "metadata": { "source": "curl-cookbook" }
  }' | jq
1inch requirements and limitations:
  • The ONEINCH_API_KEY environment variable must be set on ISCL Core.
  • If 1inch is unavailable (no API key, rate limited, network error), ISCL automatically falls back to Uniswap V3 silently.
  • swap_exact_out is not supported by the 1inch API v6 — it always uses Uniswap V3.
  • The 1inch router address (0x111111125421cA6dc452d289314280a0f8842A65) is the same on all 4 supported chains.

Recipe 5: Web Approval Workflow

When ISCL_APPROVAL_MODE=web, the approve-request endpoint blocks until a user approves or denies via the web dashboard or HTTP API. This requires two terminals.
1

Terminal 1: Submit the intent (blocks until approved)

curl -s -X POST http://localhost:3100/v1/tx/approve-request \
  -H "Content-Type: application/json" \
  -d '{
    "version": "1",
    "id": "990e8400-e29b-41d4-a716-446655440005",
    "timestamp": 1700000000000,
    "chain": { "type": "evm", "chainId": 8453 },
    "wallet": { "address": "0xYourWalletAddress" },
    "action": {
      "type": "transfer",
      "asset": {
        "kind": "erc20",
        "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
        "symbol": "USDC",
        "decimals": 6
      },
      "to": "0xRecipientAddress",
      "amount": "100000000"
    },
    "constraints": {
      "maxGasWei": "1000000000000000",
      "deadline": 1700003600,
      "maxSlippageBps": 0
    },
    "metadata": { "source": "curl-cookbook" }
  }' | jq
# This call blocks waiting for approval...
2

Terminal 2: List pending approvals

curl -s http://localhost:3100/v1/approvals/pending | jq
3

Terminal 2: Approve the request

curl -s -X POST http://localhost:3100/v1/approvals/req_abc123/decide \
  -H "Content-Type: application/json" \
  -d '{ "approved": true }' | jq
Terminal 1 unblocks and returns the approval token.
4

Alternative: Use the browser dashboard

Open http://localhost:3100/approval-ui in a browser. The dashboard automatically polls for pending requests and displays Approve / Deny buttons with risk color coding.
Pending approvals have a 300-second (5-minute) TTL. If not decided within that window, the request times out and the blocked approve-request call returns an error.

Recipe 6: Check Audit History

View recent transaction events from the append-only audit trail.
curl -s 'http://localhost:3100/v1/approvals/history?limit=5' | jq
The limit query parameter accepts values from 1 to 100. Events are returned in reverse chronological order (ORDER BY timestamp DESC).

Recipe 7: Skill Management

Register, inspect, and revoke skills through the Skill Registry API.
curl -s -X POST http://localhost:3100/v1/skills/register \
  -H "Content-Type: application/json" \
  -d '{
    "version": "1",
    "name": "my-rebalancer",
    "publisher": {
      "name": "Operator",
      "address": "0xPublisherAddress",
      "contact": "[email protected]"
    },
    "permissions": {
      "actions": ["transfer"],
      "chains": [8453],
      "network": false,
      "filesystem": false
    },
    "sandbox": {
      "memoryMb": 128,
      "timeoutMs": 30000,
      "allowSpawn": false
    },
    "files": [
      { "path": "run.mjs", "sha256": "abc123..." }
    ],
    "signature": "0x..."
  }' | jq
curl -s http://localhost:3100/v1/skills | jq
curl -s http://localhost:3100/v1/skills/my-rebalancer | jq
curl -s -X DELETE http://localhost:3100/v1/skills/my-rebalancer | jq

Recipe 8: Health Check

Verify that ISCL Core is running and responsive.
curl -s http://localhost:3100/v1/health | jq

Common Patterns

Generating UUIDs for intent IDs

Every TxIntent requires a unique id field in UUID format.
# macOS / Linux
uuidgen | tr '[:upper:]' '[:lower:]'

Computing deadlines

The constraints.deadline field is a Unix timestamp in seconds. Set it to a future time to prevent stale intents from being executed.
# 1 hour from now
echo $(( $(date +%s) + 3600 ))

Token amount conversion

All token amounts are specified as strings in their smallest unit (base unit). Use this table for reference:
TokenDecimals1 Unit in BaseExample
USDC61000000100 USDC = "100000000"
WETH1810000000000000000000.1 WETH = "100000000000000000"
DAI18100000000000000000050 DAI = "50000000000000000000"
Always use strings for amount values (e.g., "100000000" not 100000000). Large numbers exceed JavaScript’s safe integer range and must be represented as strings to avoid precision loss.

Error handling

Check HTTP status codes to determine the outcome of each request:
StatusMeaning
200Success
400Invalid intent (schema validation failure)
403Policy denied the operation
502RPC error (check RPC configuration)
For detailed error shapes and recovery guidance, see the Error Catalog.

References