> ## Documentation Index
> Fetch the complete documentation index at: https://docs.clavion.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# API Cookbook

> End-to-end curl workflows for common ISCL operations

End-to-end curl workflows for common ISCL operations. Each recipe shows the complete sequence of API calls from intent to confirmation.

<Note>
  **Base URL:** `http://localhost:3100`

  **Prerequisites:** ISCL Core running with at least one wallet imported and RPC configured.
</Note>

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

Transfer 100 USDC on Base chain.

<Steps>
  <Step title="Check the sender's balance">
    <CodeGroup>
      ```bash Request theme={null}
      curl -s http://localhost:3100/v1/balance/0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913/0xYourWalletAddress?chainId=8453 | jq
      ```

      ```json Response theme={null}
      {
        "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
        "account": "0xYourWalletAddress",
        "balance": "500000000",
        "chainId": 8453
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="Request approval (build + preflight + approve)">
    <CodeGroup>
      ```bash Request theme={null}
      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
      ```

      ```json Response theme={null}
      {
        "intentId": "550e8400-e29b-41d4-a716-446655440001",
        "approved": true,
        "approvalTokenId": "tok_abc123...",
        "txRequestHash": "0x1234...abcd",
        "description": "Transfer 100000000 of ERC-20 0x8335... to 0xReci...",
        "riskScore": 15,
        "warnings": []
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="Sign and broadcast">
    <CodeGroup>
      ```bash Request theme={null}
      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
      ```

      ```json Response theme={null}
      {
        "intentId": "550e8400-e29b-41d4-a716-446655440001",
        "signedTx": "0x02f8...",
        "txHash": "0xdef456...",
        "broadcast": true,
        "broadcastError": null
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="Check the transaction receipt">
    <CodeGroup>
      ```bash Request theme={null}
      curl -s http://localhost:3100/v1/tx/0xdef456... | jq
      ```

      ```json Response theme={null}
      {
        "txHash": "0xdef456...",
        "status": "success",
        "blockNumber": 12345678,
        "gasUsed": "52000"
      }
      ```
    </CodeGroup>
  </Step>
</Steps>

***

## 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.

<Steps>
  <Step title="Approve the transfer">
    ```bash theme={null}
    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')
    ```
  </Step>

  <Step title="Sign and send using the extracted token">
    ```bash theme={null}
    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
    ```
  </Step>
</Steps>

<Tip>
  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).
</Tip>

***

## Recipe 3: DEX Swap (Uniswap V3)

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

<Steps>
  <Step title="Submit the swap intent for approval">
    <CodeGroup>
      ```bash Request theme={null}
      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
      ```

      ```json Response theme={null}
      {
        "intentId": "770e8400-e29b-41d4-a716-446655440003",
        "approved": true,
        "approvalTokenId": "tok_swap123...",
        "txRequestHash": "0x5678...efgh",
        "description": "Swap 100000000000000000 WETH for USDC (min 250000000)",
        "riskScore": 25,
        "warnings": []
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="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.
  </Step>
</Steps>

<Note>
  The Uniswap V3 router address varies by chain:

  * **Base (8453):** `0x2626664c2603336E57B271c5C0b26F421741e481`
  * **Ethereum, Optimism, Arbitrum:** `0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45`
</Note>

***

## 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.

<CodeGroup>
  ```bash Request theme={null}
  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
  ```

  ```json Response theme={null}
  {
    "intentId": "880e8400-e29b-41d4-a716-446655440004",
    "approved": true,
    "approvalTokenId": "tok_1inch456...",
    "txRequestHash": "0x9abc...ijkl",
    "description": "Swap 100000000000000000 WETH for USDC via 1inch (min 250000000)",
    "riskScore": 25,
    "warnings": []
  }
  ```
</CodeGroup>

<Warning>
  **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.
</Warning>

***

## 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.

<Steps>
  <Step title="Terminal 1: Submit the intent (blocks until approved)">
    ```bash theme={null}
    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...
    ```
  </Step>

  <Step title="Terminal 2: List pending approvals">
    <CodeGroup>
      ```bash Request theme={null}
      curl -s http://localhost:3100/v1/approvals/pending | jq
      ```

      ```json Response theme={null}
      [
        {
          "requestId": "req_abc123",
          "summary": {
            "action": "Transfer 100000000 of ERC-20 0x8335... to 0xReci...",
            "riskScore": 15,
            "warnings": []
          },
          "ttlSeconds": 285
        }
      ]
      ```
    </CodeGroup>
  </Step>

  <Step title="Terminal 2: Approve the request">
    ```bash theme={null}
    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.
  </Step>

  <Step title="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.
  </Step>
</Steps>

<Tip>
  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.
</Tip>

***

## Recipe 6: Check Audit History

View recent transaction events from the append-only audit trail.

<CodeGroup>
  ```bash Request theme={null}
  curl -s 'http://localhost:3100/v1/approvals/history?limit=5' | jq
  ```

  ```json Response theme={null}
  [
    {
      "id": "evt_001",
      "timestamp": 1700000001000,
      "intentId": "550e8400-...",
      "event": "policy_evaluated",
      "data": { "decision": "allow", "reasons": [] }
    },
    {
      "id": "evt_002",
      "timestamp": 1700000001500,
      "intentId": "550e8400-...",
      "event": "signed",
      "data": { "txHash": "0xdef456..." }
    }
  ]
  ```
</CodeGroup>

<Note>
  The `limit` query parameter accepts values from 1 to 100. Events are returned in reverse chronological order (`ORDER BY timestamp DESC`).
</Note>

***

## Recipe 7: Skill Management

Register, inspect, and revoke skills through the Skill Registry API.

<Accordion title="Register a skill">
  <CodeGroup>
    ```bash Request theme={null}
    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": "operator@example.com"
        },
        "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
    ```

    ```json Response theme={null}
    {
      "name": "my-rebalancer",
      "registered": true
    }
    ```
  </CodeGroup>
</Accordion>

<Accordion title="List registered skills">
  <CodeGroup>
    ```bash Request theme={null}
    curl -s http://localhost:3100/v1/skills | jq
    ```

    ```json Response theme={null}
    [
      {
        "name": "my-rebalancer",
        "publisher": "Operator",
        "actions": ["transfer"],
        "chains": [8453]
      }
    ]
    ```
  </CodeGroup>
</Accordion>

<Accordion title="Get skill details">
  <CodeGroup>
    ```bash Request theme={null}
    curl -s http://localhost:3100/v1/skills/my-rebalancer | jq
    ```

    ```json Response theme={null}
    {
      "version": "1",
      "name": "my-rebalancer",
      "publisher": {
        "name": "Operator",
        "address": "0xPublisherAddress",
        "contact": "operator@example.com"
      },
      "permissions": {
        "actions": ["transfer"],
        "chains": [8453],
        "network": false,
        "filesystem": false
      },
      "sandbox": {
        "memoryMb": 128,
        "timeoutMs": 30000,
        "allowSpawn": false
      }
    }
    ```
  </CodeGroup>
</Accordion>

<Accordion title="Revoke a skill">
  <CodeGroup>
    ```bash Request theme={null}
    curl -s -X DELETE http://localhost:3100/v1/skills/my-rebalancer | jq
    ```

    ```json Response theme={null}
    {
      "name": "my-rebalancer",
      "revoked": true
    }
    ```
  </CodeGroup>
</Accordion>

***

## Recipe 8: Health Check

Verify that ISCL Core is running and responsive.

<CodeGroup>
  ```bash Request theme={null}
  curl -s http://localhost:3100/v1/health | jq
  ```

  ```json Response theme={null}
  {
    "status": "ok",
    "version": "0.1.0",
    "uptime": 3600.5
  }
  ```
</CodeGroup>

***

## Common Patterns

### Generating UUIDs for intent IDs

Every TxIntent requires a unique `id` field in UUID format.

```bash theme={null}
# 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.

```bash theme={null}
# 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:

| Token | Decimals | 1 Unit in Base        | Example                           |
| ----- | -------- | --------------------- | --------------------------------- |
| USDC  | 6        | `1000000`             | 100 USDC = `"100000000"`          |
| WETH  | 18       | `1000000000000000000` | 0.1 WETH = `"100000000000000000"` |
| DAI   | 18       | `1000000000000000000` | 50 DAI = `"50000000000000000000"` |

<Warning>
  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.
</Warning>

### Error handling

Check HTTP status codes to determine the outcome of each request:

| Status  | Meaning                                    |
| ------- | ------------------------------------------ |
| **200** | Success                                    |
| **400** | Invalid intent (schema validation failure) |
| **403** | Policy denied the operation                |
| **502** | RPC error (check RPC configuration)        |

For detailed error shapes and recovery guidance, see the [Error Catalog](/reference/error-codes).

***

## References

<CardGroup cols={2}>
  <Card title="REST API Reference" icon="code" href="/reference/rest-api">
    Full endpoint documentation with request/response schemas
  </Card>

  <Card title="Error Catalog" icon="triangle-exclamation" href="/reference/error-codes">
    Error shapes and recovery guidance
  </Card>

  <Card title="TxIntent Schema" icon="file-code" href="/reference/txintent-schema">
    TxIntent and action type schema specification
  </Card>

  <Card title="Configuration" icon="gear" href="/reference/config-reference">
    Environment variables and policy configuration
  </Card>
</CardGroup>
