Examples

All examples use the public Caddy gateway URL. Replace placeholders before running paid retries.

MCP discovery tool

After connecting to https://canix402-mcp.compx.io/mcp, call canix_get_discovery for the same endpoint catalog as HTTP /discovery (free tool).

# Example: MCP client invokes canix_get_discovery (no payment required)
# Paid tools: first call returns PAYMENT_REQUIRED; retry with paymentSignature

Discovery fetch (HTTP)

curl -s https://canix402-api.compx.io/discovery | jq '.data.endpoints[] | {path, access, summary}'

OpenAPI fetch

curl -s https://canix402-api.compx.io/openapi.json | jq '.info, (.paths | keys)'

MCP paid tool retry shape

Paid MCP tools mirror the HTTP x402 flow. First invocation returns payment metadata; retry passes the signed payload as paymentSignature (tool argument, not an HTTP header).

// 1) canix_list_opportunities({ limit: 10 })
//    -> PAYMENT_REQUIRED + paymentRequiredHeader metadata

// 2) canix_list_opportunities({
//      limit: 10,
//      paymentSignature: "<base64-json-payload>"
//    })
//    -> opportunity data

Signed retry shape (HTTP)

The website does not sign payments. Your agent/client wallet must build this payload.

{
  "x402Version": 2,
  "scheme": "exact",
  "network": "algorand-mainnet",
  "accepted": { /* selected accept option from PAYMENT-REQUIRED */ },
  "payload": {
    "paymentGroup": ["<base64-signed-txn>"],
    "paymentIndex": 0
  },
  "paymentRequired": { /* decoded PAYMENT-REQUIRED */ }
}

Sample response payloads

These are illustrative snapshots of the normalized response contract, not live paid data. Opportunity rows include executionShapes, compatibleExitShapes, and (for Réti) entryRequirements / capacity. Numeric values follow the precision contract (6 decimal places by default, up to 12 for small non-zero values). Live responses vary in values and row count; the shape and field semantics match. The full machine-readable schema and examples are published in OpenAPI.

Aggregated opportunities

GET /opportunities — top opportunities across all protocols, ranked by APY.

curl -i https://canix402-api.compx.io/opportunities

Sample response (200)

{
  "data": [
    {
      "protocol": "tinyman",
      "opportunityType": "lp",
      "opportunityId": "tinyman:pool:1002541853",
      "assetPair": "ALGO/USDC",
      "assetIds": [
        0,
        31566704
      ],
      "apr": 10.512,
      "apy": 12.5,
      "yieldBasis": "apy",
      "tvlUsd": 2450000.5,
      "sourceTimestamp": "2026-07-06T09:00:00.000Z",
      "fetchedAt": "2026-07-06T09:00:00.000Z",
      "notes": "sourceTimestamp reflects fetch time; upstream does not expose a per-row update time.",
      "executionReady": true,
      "executionShapes": [
        {
          "shapeKey": "mainnet:tinyman:v2:addLiquidity:flexible",
          "protocol": "tinyman",
          "protocolVersion": "v2",
          "action": "addLiquidity",
          "variant": "flexible",
          "title": "Tinyman v2 flexible add liquidity",
          "summary": "Adds two-sided liquidity to an existing Tinyman AMM v2 pool using flexible amounts. Generates the documented 3-transaction group (asset1 transfer, asset2 transfer/payment, add_liquidity app call) as unsigned transactions.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "assetAId",
            "assetAAmount",
            "assetBId",
            "assetBAmount",
            "maxSlippageBps"
          ],
          "requiredAssetIds": [
            0,
            31566704
          ],
          "inputHints": {
            "assetAId": 0,
            "assetBId": 31566704,
            "poolId": "tinyman:pool:1002541853"
          }
        },
        {
          "shapeKey": "mainnet:tinyman:v2:addLiquidity:initial",
          "protocol": "tinyman",
          "protocolVersion": "v2",
          "action": "addLiquidity",
          "variant": "initial",
          "title": "Tinyman v2 initial add liquidity",
          "summary": "Adds the first liquidity to a bootstrapped but empty Tinyman AMM v2 pool. Generates the documented 3-transaction group (asset1 transfer, asset2 transfer/payment, add_initial_liquidity app call) as unsigned transactions.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "assetAId",
            "assetAAmount",
            "assetBId",
            "assetBAmount",
            "maxSlippageBps"
          ],
          "requiredAssetIds": [
            0,
            31566704
          ],
          "inputHints": {
            "assetAId": 0,
            "assetBId": 31566704,
            "poolId": "tinyman:pool:1002541853"
          }
        },
        {
          "shapeKey": "mainnet:tinyman:v2:addLiquidity:singleAsset",
          "protocol": "tinyman",
          "protocolVersion": "v2",
          "action": "addLiquidity",
          "variant": "singleAsset",
          "title": "Tinyman v2 single-asset add liquidity",
          "summary": "Adds one-sided liquidity to an existing Tinyman AMM v2 pool. Tinyman performs an internal swap to balance the deposit and mints pool tokens. Generates the documented 2-transaction group (asset transfer/payment, add_liquidity app call) as unsigned transactions.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "assetAId",
            "assetBId",
            "depositAssetId",
            "depositAmount",
            "maxSlippageBps"
          ],
          "requiredAssetIds": [
            0,
            31566704
          ],
          "inputHints": {
            "assetAId": 0,
            "assetBId": 31566704,
            "poolId": "tinyman:pool:1002541853"
          }
        }
      ],
      "compatibleExitShapes": []
    },
    {
      "protocol": "compx",
      "opportunityType": "lending",
      "opportunityId": "compx-lending-3491050310",
      "assetPair": "USDC",
      "assetIds": [
        31566704,
        3491050538
      ],
      "apy": 1.4,
      "apr": 1.4,
      "borrowApr": 8.5,
      "yieldBasis": "apr",
      "tvlUsd": 137.181191,
      "sourceTimestamp": "2026-07-14T20:47:17.000Z",
      "fetchedAt": "2026-07-17T12:41:14.931Z",
      "notes": "CompX lending market 3491050310; util=4.4%; ltv=70.0%; APR-derived yields; borrowApr is the borrow cost.",
      "executionReady": true,
      "executionShapes": [
        {
          "shapeKey": "mainnet:compx:v1:deposit:asa",
          "protocol": "compx",
          "protocolVersion": "v1",
          "action": "deposit",
          "variant": "asa",
          "title": "CompX v1 ASA lending deposit",
          "summary": "Deposits a base ASA into a CompX lending market and receives LST tokens.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "marketAppId",
            "amount"
          ],
          "requiredAssetIds": [
            31566704
          ],
          "inputHints": {
            "assetId": 31566704,
            "marketAppId": 3491050310
          }
        },
        {
          "shapeKey": "mainnet:compx:v1:borrow:asa",
          "protocol": "compx",
          "protocolVersion": "v1",
          "action": "borrow",
          "variant": "asa",
          "title": "CompX v1 ASA lending borrow",
          "summary": "Borrows a base ASA from a CompX lending market against LST collateral.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "marketAppId",
            "borrowAmount",
            "collateralAmount"
          ],
          "requiredAssetIds": [
            31566704
          ],
          "inputHints": {
            "assetId": 31566704,
            "marketAppId": 3491050310
          }
        }
      ],
      "compatibleExitShapes": [
        {
          "shapeKey": "mainnet:compx:v1:withdraw:asa",
          "protocol": "compx",
          "protocolVersion": "v1",
          "action": "withdraw",
          "variant": "asa",
          "title": "CompX v1 ASA lending withdraw",
          "summary": "Withdraws a base ASA from a CompX lending market by redeeming LST tokens.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "marketAppId",
            "amount"
          ],
          "requiredAssetIds": [
            31566704
          ],
          "inputHints": {
            "assetId": 31566704,
            "marketAppId": 3491050310
          }
        },
        {
          "shapeKey": "mainnet:compx:v1:repay:asa",
          "protocol": "compx",
          "protocolVersion": "v1",
          "action": "repay",
          "variant": "asa",
          "title": "CompX v1 ASA lending repay",
          "summary": "Repays outstanding base-ASA debt on a CompX lending market.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "marketAppId",
            "amount"
          ],
          "requiredAssetIds": [
            31566704
          ],
          "inputHints": {
            "assetId": 31566704,
            "marketAppId": 3491050310
          }
        }
      ]
    },
    {
      "protocol": "reti",
      "opportunityType": "staking",
      "opportunityId": "reti-staking-12",
      "assetPair": "ALGO",
      "assetIds": [
        0
      ],
      "apy": 8.5,
      "apr": 8.5,
      "yieldBasis": "apr",
      "tvlUsd": 125000.5,
      "sourceTimestamp": "2026-07-23T10:00:00.000Z",
      "fetchedAt": "2026-07-23T10:00:00.000Z",
      "entryRequirements": {
        "minAmount": {
          "assetId": 0,
          "amount": "1000000000"
        },
        "eligibilityFullyCheckable": true
      },
      "capacity": {
        "stakerSlotsRemaining": 20,
        "algoRoomMicroAlgos": "50000000000",
        "acceptingStake": true
      },
      "notes": "Réti validator-level consensus staking; quote-time eligibility checks are authoritative.",
      "executionReady": true,
      "executionShapes": [
        {
          "shapeKey": "mainnet:reti:v1:stake:algo",
          "protocol": "reti",
          "protocolVersion": "v1",
          "action": "stake",
          "variant": "algo",
          "title": "Réti ALGO stake to validator",
          "summary": "Stakes ALGO to a Réti validator via ValidatorRegistry.addStake. The registry allocates into one of the validator's pools. Builds gas ×2, ALGO payment to the registry, addStake, and optional reward-token opt-in. Enforces minEntryStake, capacity, and entry gates at quote time.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "validatorId",
            "amount"
          ],
          "requiredAssetIds": [
            0
          ],
          "inputHints": {
            "validatorId": 12,
            "assetId": 0,
            "depositAssetId": 0
          }
        }
      ],
      "compatibleExitShapes": [
        {
          "shapeKey": "mainnet:reti:v1:unstake:algo",
          "protocol": "reti",
          "protocolVersion": "v1",
          "action": "unstake",
          "variant": "algo",
          "title": "Réti ALGO unstake from pool",
          "summary": "Removes ALGO stake from a Réti staking pool via StakingPool.removeStake. Builds gas ×2, removeStake, and optional reward-token opt-in. Partial unstake must leave at least minEntryStake (or unstake the full balance).",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "validatorId",
            "poolAppId",
            "amount"
          ],
          "requiredAssetIds": [
            0
          ],
          "inputHints": {
            "validatorId": 12,
            "assetId": 0,
            "depositAssetId": 0
          }
        }
      ]
    },
    {
      "protocol": "folks-finance",
      "opportunityType": "lending",
      "opportunityId": "folks-lending-971254667",
      "assetPair": "USDC",
      "assetIds": [
        31566704
      ],
      "apy": 6.06,
      "apr": 5.9,
      "borrowApr": 8.25,
      "yieldBasis": "apy",
      "tvlUsd": 29846.609471,
      "sourceTimestamp": "2026-07-06T08:55:00.000Z",
      "fetchedAt": "2026-07-06T09:00:00.000Z",
      "executionReady": true,
      "executionShapes": [
        {
          "shapeKey": "mainnet:folks-finance:v2:setup:depositEscrow",
          "protocol": "folks-finance",
          "protocolVersion": "v2",
          "action": "setup",
          "variant": "depositEscrow",
          "title": "Folks Finance v2 add deposit escrow",
          "summary": "Creates and funds a new Folks Finance deposit escrow for the user. Returns a 3-transaction group that must be signed by both the user and the generated escrow account.",
          "order": 0,
          "requiredInputs": [
            "userAddress"
          ],
          "requiredAssetIds": [
            31566704
          ],
          "inputHints": {
            "poolAppId": 971254667,
            "assetId": 31566704
          }
        },
        {
          "shapeKey": "mainnet:folks-finance:v2:setup:optEscrowAsset",
          "protocol": "folks-finance",
          "protocolVersion": "v2",
          "action": "setup",
          "variant": "optEscrowAsset",
          "title": "Folks Finance v2 opt deposit escrow into pool fAsset",
          "summary": "Funds and opts an existing Folks Finance deposit escrow into a pool fAsset so it can receive deposits.",
          "order": 1,
          "prerequisiteShapeKeys": [
            "mainnet:folks-finance:v2:setup:depositEscrow"
          ],
          "requiredInputs": [
            "userAddress",
            "escrowAddress"
          ],
          "requiredAssetIds": [
            31566704
          ],
          "inputHints": {
            "poolAppId": 971254667,
            "assetId": 31566704
          }
        },
        {
          "shapeKey": "mainnet:folks-finance:v2:deposit:escrow",
          "protocol": "folks-finance",
          "protocolVersion": "v2",
          "action": "deposit",
          "variant": "escrow",
          "title": "Folks Finance v2 escrow deposit",
          "summary": "Deposits an underlying asset into a Folks Finance lending pool via a deposit escrow. fAssets accrue in the escrow rather than the user's wallet. Generates the SDK deposit group (asset transfer + deposit app call), optionally prefixed with OpUp.",
          "order": 2,
          "prerequisiteShapeKeys": [
            "mainnet:folks-finance:v2:setup:optEscrowAsset"
          ],
          "requiredInputs": [
            "userAddress",
            "assetAmount"
          ],
          "requiredAssetIds": [
            31566704
          ],
          "inputHints": {
            "poolAppId": 971254667,
            "assetId": 31566704
          }
        }
      ],
      "compatibleExitShapes": []
    }
  ],
  "meta": {
    "limit": 10,
    "offset": 0,
    "includeInactive": false,
    "paymentRequired": true
  }
}

Filtered search

GET /opportunities/search — caller-filtered opportunities by platform, type, APY, and TVL.

curl -i 'https://canix402-api.compx.io/opportunities/search?platform=tinyman&minApy=0.05'

Sample response (200)

{
  "data": [
    {
      "protocol": "tinyman",
      "opportunityType": "lp",
      "opportunityId": "tinyman:pool:1002541853",
      "assetPair": "ALGO/USDC",
      "assetIds": [
        0,
        31566704
      ],
      "apr": 10.512,
      "apy": 12.5,
      "yieldBasis": "apy",
      "tvlUsd": 2450000.5,
      "sourceTimestamp": "2026-07-06T09:00:00.000Z",
      "fetchedAt": "2026-07-06T09:00:00.000Z",
      "executionReady": true,
      "executionShapes": [
        {
          "shapeKey": "mainnet:tinyman:v2:addLiquidity:flexible",
          "protocol": "tinyman",
          "protocolVersion": "v2",
          "action": "addLiquidity",
          "variant": "flexible",
          "title": "Tinyman v2 flexible add liquidity",
          "summary": "Adds two-sided liquidity to an existing Tinyman AMM v2 pool using flexible amounts. Generates the documented 3-transaction group (asset1 transfer, asset2 transfer/payment, add_liquidity app call) as unsigned transactions.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "assetAId",
            "assetAAmount",
            "assetBId",
            "assetBAmount",
            "maxSlippageBps"
          ],
          "requiredAssetIds": [
            0,
            31566704
          ],
          "inputHints": {
            "assetAId": 0,
            "assetBId": 31566704,
            "poolId": "tinyman:pool:1002541853"
          }
        },
        {
          "shapeKey": "mainnet:tinyman:v2:addLiquidity:initial",
          "protocol": "tinyman",
          "protocolVersion": "v2",
          "action": "addLiquidity",
          "variant": "initial",
          "title": "Tinyman v2 initial add liquidity",
          "summary": "Adds the first liquidity to a bootstrapped but empty Tinyman AMM v2 pool. Generates the documented 3-transaction group (asset1 transfer, asset2 transfer/payment, add_initial_liquidity app call) as unsigned transactions.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "assetAId",
            "assetAAmount",
            "assetBId",
            "assetBAmount",
            "maxSlippageBps"
          ],
          "requiredAssetIds": [
            0,
            31566704
          ],
          "inputHints": {
            "assetAId": 0,
            "assetBId": 31566704,
            "poolId": "tinyman:pool:1002541853"
          }
        },
        {
          "shapeKey": "mainnet:tinyman:v2:addLiquidity:singleAsset",
          "protocol": "tinyman",
          "protocolVersion": "v2",
          "action": "addLiquidity",
          "variant": "singleAsset",
          "title": "Tinyman v2 single-asset add liquidity",
          "summary": "Adds one-sided liquidity to an existing Tinyman AMM v2 pool. Tinyman performs an internal swap to balance the deposit and mints pool tokens. Generates the documented 2-transaction group (asset transfer/payment, add_liquidity app call) as unsigned transactions.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "assetAId",
            "assetBId",
            "depositAssetId",
            "depositAmount",
            "maxSlippageBps"
          ],
          "requiredAssetIds": [
            0,
            31566704
          ],
          "inputHints": {
            "assetAId": 0,
            "assetBId": 31566704,
            "poolId": "tinyman:pool:1002541853"
          }
        }
      ],
      "compatibleExitShapes": []
    }
  ],
  "meta": {
    "limit": 25,
    "offset": 0,
    "includeInactive": false,
    "paymentRequired": true
  }
}

Wallet-personalized opportunities

GET /opportunities/personalized — opportunities tuned to a wallet's held assets. Matching applies POST /eligibility rules so full or gated venues are not recommended as enterable. Rows include canEnter and eligibilityFullyCheckable; meta.eligibilityEndpoint is /eligibility.

curl -i 'https://canix402-api.compx.io/opportunities/personalized?address=YOUR_ALGORAND_ADDRESS'

Sample response (200)

{
  "data": [
    {
      "protocol": "folks-finance",
      "opportunityType": "lending",
      "opportunityId": "folks-lending-971365552",
      "assetPair": "ALGO",
      "assetIds": [
        0
      ],
      "apy": 4.25,
      "yieldBasis": "apy",
      "tvlUsd": 1875000.25,
      "sourceTimestamp": "2026-07-06T08:55:00.000Z",
      "fetchedAt": "2026-07-06T09:00:00.000Z",
      "executionReady": true,
      "executionShapes": [
        {
          "shapeKey": "mainnet:folks-finance:v2:setup:depositEscrow",
          "protocol": "folks-finance",
          "protocolVersion": "v2",
          "action": "setup",
          "variant": "depositEscrow",
          "title": "Folks Finance v2 add deposit escrow",
          "summary": "Creates and funds a new Folks Finance deposit escrow for the user. Returns a 3-transaction group that must be signed by both the user and the generated escrow account.",
          "order": 0,
          "requiredInputs": [
            "userAddress"
          ],
          "requiredAssetIds": [
            0
          ],
          "inputHints": {
            "poolAppId": 971365552,
            "assetId": 0
          }
        },
        {
          "shapeKey": "mainnet:folks-finance:v2:setup:optEscrowAsset",
          "protocol": "folks-finance",
          "protocolVersion": "v2",
          "action": "setup",
          "variant": "optEscrowAsset",
          "title": "Folks Finance v2 opt deposit escrow into pool fAsset",
          "summary": "Funds and opts an existing Folks Finance deposit escrow into a pool fAsset so it can receive deposits.",
          "order": 1,
          "prerequisiteShapeKeys": [
            "mainnet:folks-finance:v2:setup:depositEscrow"
          ],
          "requiredInputs": [
            "userAddress",
            "escrowAddress"
          ],
          "requiredAssetIds": [
            0
          ],
          "inputHints": {
            "poolAppId": 971365552,
            "assetId": 0
          }
        },
        {
          "shapeKey": "mainnet:folks-finance:v2:deposit:escrow",
          "protocol": "folks-finance",
          "protocolVersion": "v2",
          "action": "deposit",
          "variant": "escrow",
          "title": "Folks Finance v2 escrow deposit",
          "summary": "Deposits an underlying asset into a Folks Finance lending pool via a deposit escrow. fAssets accrue in the escrow rather than the user's wallet. Generates the SDK deposit group (asset transfer + deposit app call), optionally prefixed with OpUp.",
          "order": 2,
          "prerequisiteShapeKeys": [
            "mainnet:folks-finance:v2:setup:optEscrowAsset"
          ],
          "requiredInputs": [
            "userAddress",
            "assetAmount"
          ],
          "requiredAssetIds": [
            0
          ],
          "inputHints": {
            "poolAppId": 971365552,
            "assetId": 0
          }
        }
      ],
      "compatibleExitShapes": [],
      "canEnter": true,
      "eligibilityFullyCheckable": true
    },
    {
      "protocol": "tinyman",
      "opportunityType": "lp",
      "opportunityId": "tinyman:pool:1002541853",
      "assetPair": "ALGO/USDC",
      "assetIds": [
        0,
        31566704
      ],
      "apr": 10.512,
      "apy": 12.5,
      "yieldBasis": "apy",
      "tvlUsd": 2450000.5,
      "sourceTimestamp": "2026-07-06T09:00:00.000Z",
      "fetchedAt": "2026-07-06T09:00:00.000Z",
      "executionReady": true,
      "executionShapes": [
        {
          "shapeKey": "mainnet:tinyman:v2:addLiquidity:flexible",
          "protocol": "tinyman",
          "protocolVersion": "v2",
          "action": "addLiquidity",
          "variant": "flexible",
          "title": "Tinyman v2 flexible add liquidity",
          "summary": "Adds two-sided liquidity to an existing Tinyman AMM v2 pool using flexible amounts. Generates the documented 3-transaction group (asset1 transfer, asset2 transfer/payment, add_liquidity app call) as unsigned transactions.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "assetAId",
            "assetAAmount",
            "assetBId",
            "assetBAmount",
            "maxSlippageBps"
          ],
          "requiredAssetIds": [
            0,
            31566704
          ],
          "inputHints": {
            "assetAId": 0,
            "assetBId": 31566704,
            "poolId": "tinyman:pool:1002541853"
          }
        },
        {
          "shapeKey": "mainnet:tinyman:v2:addLiquidity:initial",
          "protocol": "tinyman",
          "protocolVersion": "v2",
          "action": "addLiquidity",
          "variant": "initial",
          "title": "Tinyman v2 initial add liquidity",
          "summary": "Adds the first liquidity to a bootstrapped but empty Tinyman AMM v2 pool. Generates the documented 3-transaction group (asset1 transfer, asset2 transfer/payment, add_initial_liquidity app call) as unsigned transactions.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "assetAId",
            "assetAAmount",
            "assetBId",
            "assetBAmount",
            "maxSlippageBps"
          ],
          "requiredAssetIds": [
            0,
            31566704
          ],
          "inputHints": {
            "assetAId": 0,
            "assetBId": 31566704,
            "poolId": "tinyman:pool:1002541853"
          }
        },
        {
          "shapeKey": "mainnet:tinyman:v2:addLiquidity:singleAsset",
          "protocol": "tinyman",
          "protocolVersion": "v2",
          "action": "addLiquidity",
          "variant": "singleAsset",
          "title": "Tinyman v2 single-asset add liquidity",
          "summary": "Adds one-sided liquidity to an existing Tinyman AMM v2 pool. Tinyman performs an internal swap to balance the deposit and mints pool tokens. Generates the documented 2-transaction group (asset transfer/payment, add_liquidity app call) as unsigned transactions.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "assetAId",
            "assetBId",
            "depositAssetId",
            "depositAmount",
            "maxSlippageBps"
          ],
          "requiredAssetIds": [
            0,
            31566704
          ],
          "inputHints": {
            "assetAId": 0,
            "assetBId": 31566704,
            "poolId": "tinyman:pool:1002541853"
          }
        }
      ],
      "compatibleExitShapes": [],
      "canEnter": true,
      "eligibilityFullyCheckable": true
    },
    {
      "protocol": "reti",
      "opportunityType": "staking",
      "opportunityId": "reti-staking-12",
      "assetPair": "ALGO",
      "assetIds": [
        0
      ],
      "apy": 8.5,
      "apr": 8.5,
      "yieldBasis": "apr",
      "tvlUsd": 125000.5,
      "sourceTimestamp": "2026-07-23T10:00:00.000Z",
      "fetchedAt": "2026-07-23T10:00:00.000Z",
      "entryRequirements": {
        "minAmount": {
          "assetId": 0,
          "amount": "1000000000"
        },
        "eligibilityFullyCheckable": true
      },
      "capacity": {
        "stakerSlotsRemaining": 20,
        "algoRoomMicroAlgos": "50000000000",
        "acceptingStake": true
      },
      "notes": "Réti validator-level consensus staking; quote-time eligibility checks are authoritative.",
      "executionReady": true,
      "executionShapes": [
        {
          "shapeKey": "mainnet:reti:v1:stake:algo",
          "protocol": "reti",
          "protocolVersion": "v1",
          "action": "stake",
          "variant": "algo",
          "title": "Réti ALGO stake to validator",
          "summary": "Stakes ALGO to a Réti validator via ValidatorRegistry.addStake. The registry allocates into one of the validator's pools. Builds gas ×2, ALGO payment to the registry, addStake, and optional reward-token opt-in. Enforces minEntryStake, capacity, and entry gates at quote time.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "validatorId",
            "amount"
          ],
          "requiredAssetIds": [
            0
          ],
          "inputHints": {
            "validatorId": 12,
            "assetId": 0,
            "depositAssetId": 0
          }
        }
      ],
      "compatibleExitShapes": [
        {
          "shapeKey": "mainnet:reti:v1:unstake:algo",
          "protocol": "reti",
          "protocolVersion": "v1",
          "action": "unstake",
          "variant": "algo",
          "title": "Réti ALGO unstake from pool",
          "summary": "Removes ALGO stake from a Réti staking pool via StakingPool.removeStake. Builds gas ×2, removeStake, and optional reward-token opt-in. Partial unstake must leave at least minEntryStake (or unstake the full balance).",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "validatorId",
            "poolAppId",
            "amount"
          ],
          "requiredAssetIds": [
            0
          ],
          "inputHints": {
            "validatorId": 12,
            "assetId": 0,
            "depositAssetId": 0
          }
        }
      ],
      "canEnter": true,
      "eligibilityFullyCheckable": true
    }
  ],
  "meta": {
    "limit": 10,
    "offset": 0,
    "includeInactive": false,
    "paymentRequired": true,
    "address": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    "heldAssetCount": 2,
    "eligibilityApplied": true,
    "eligibilityEndpoint": "/eligibility"
  }
}

Eligibility and capacity

POST /eligibility — check whether a wallet can enter selected opportunities before quoting. Returns canEnter, missingAssets, gates, capacity, and optional suggestedSwap. NFD/creator gates stay unresolved (eligibilityFullyCheckable: false).

curl -i https://canix402-api.compx.io/eligibility -H 'content-type: application/json' -d '{"address":"YOUR_ALGORAND_ADDRESS","opportunityIds":["reti-staking-12"]}'

Sample response (200)

{
  "data": [
    {
      "opportunityId": "reti-staking-12",
      "protocol": "reti",
      "found": true,
      "canEnter": true,
      "eligibilityFullyCheckable": true,
      "missingAssets": [],
      "gates": [],
      "capacity": {
        "stakerSlotsRemaining": 20,
        "algoRoomMicroAlgos": "50000000000",
        "acceptingStake": true
      },
      "suggestedSwap": null,
      "reasons": []
    }
  ],
  "meta": {
    "address": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    "fetchedAt": "2026-08-14T21:00:00.000Z",
    "paymentRequired": true,
    "quoteTimeAuthoritative": true
  }
}

Intent compiler (paid)

POST /plans — flagship compiler SKU (~0.25 USDC). State an allocation intent (address, budget/asset, constraints). Returns ordered steps: eligibility, optional swap hints, protocol setup chains, and enter quotes as independent unsigned groups (never merged). Reuses quotes[] / order / prerequisiteShapeKeys. Brownie and other agents should consume this route rather than forking a compiler. Canix never signs or submits.

curl -i https://canix402-api.compx.io/plans -H 'content-type: application/json' -d '{"address":"YOUR_ALGORAND_ADDRESS","budget":{"assetId":0,"amount":"1000000"},"constraints":{"noNewBorrows":true,"executionReadyOnly":true},"opportunityIds":["reti-staking-12"]}'

Sample response (200)

{
  "data": {
    "allocations": [
      {
        "opportunityId": "reti-staking-12",
        "protocol": "reti",
        "opportunityType": "staking",
        "assetPair": "ALGO",
        "apy": 8.5,
        "allocatedAmount": "1000000",
        "allocatedAssetId": 0,
        "weightBps": 10000,
        "eligibility": {
          "opportunityId": "reti-staking-12",
          "protocol": "reti",
          "found": true,
          "canEnter": true,
          "eligibilityFullyCheckable": true,
          "missingAssets": [],
          "gates": [],
          "capacity": {
            "stakerSlotsRemaining": 20,
            "algoRoomMicroAlgos": "50000000000",
            "acceptingStake": true
          },
          "suggestedSwap": null,
          "reasons": []
        },
        "executionShapes": [
          {
            "shapeKey": "mainnet:reti:v1:stake:algo",
            "protocol": "reti",
            "protocolVersion": "v1",
            "action": "stake",
            "variant": "algo",
            "title": "Réti ALGO stake to validator",
            "summary": "Stakes ALGO to a Réti validator.",
            "order": 0,
            "requiredInputs": [
              "userAddress",
              "validatorId",
              "amount"
            ],
            "requiredAssetIds": [
              0
            ],
            "inputHints": {
              "validatorId": 12,
              "assetId": 0,
              "depositAssetId": 0
            }
          }
        ],
        "steps": [
          {
            "kind": "eligibility",
            "order": 0,
            "compileStatus": "compiled",
            "warnings": []
          },
          {
            "kind": "enter",
            "order": 1,
            "compileStatus": "compiled",
            "shapeKey": "mainnet:reti:v1:stake:algo",
            "quoteRequest": {
              "shapeKey": "mainnet:reti:v1:stake:algo",
              "input": {
                "userAddress": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
                "validatorId": 12,
                "assetId": 0,
                "depositAssetId": 0,
                "amount": "1000000"
              }
            },
            "quote": {
              "shapeKey": "mainnet:reti:v1:stake:algo",
              "shapeVersion": "1.0.0",
              "identity": {
                "network": "mainnet",
                "protocol": "reti",
                "protocolVersion": "v1",
                "action": "stake",
                "variant": "algo"
              },
              "createdAt": "2026-08-16T10:00:00.000Z",
              "expiresAt": "2026-08-16T10:00:30.000Z",
              "transactions": [
                {
                  "type": "pay",
                  "sender": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
                  "fee": "1000",
                  "groupPresent": true
                }
              ],
              "encodedTransactions": [
                "<base64-unsigned-txn>"
              ],
              "warnings": [],
              "metadata": {}
            },
            "warnings": []
          }
        ],
        "quotes": [
          {
            "shapeKey": "mainnet:reti:v1:stake:algo",
            "input": {
              "userAddress": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
              "validatorId": 12,
              "assetId": 0,
              "depositAssetId": 0,
              "amount": "1000000"
            }
          }
        ]
      }
    ],
    "blocked": [],
    "expectedPositionDelta": {
      "summary": "Enter 1000000 of asset 0 into reti-staking-12 (reti).",
      "entries": [
        {
          "opportunityId": "reti-staking-12",
          "protocol": "reti",
          "assetId": 0,
          "amount": "1000000",
          "action": "enter"
        }
      ]
    },
    "fees": {
      "x402Usdc": "0.25",
      "estimatedNetworkFeeMicroAlgos": "1000",
      "estimatedNetworkFeeUsd": null
    },
    "expiresAt": "2026-08-16T10:00:30.000Z",
    "warnings": []
  },
  "meta": {
    "address": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    "budget": {
      "assetId": 0,
      "amount": "1000000"
    },
    "fetchedAt": "2026-08-16T10:00:00.000Z",
    "paymentRequired": true,
    "executionSubmitted": false,
    "quoteTimeAuthoritative": true,
    "eligibilityEndpoint": "/eligibility"
  }
}

Protocol-specific opportunities

GET /protocols/{protocol}/opportunities — opportunities for a single protocol. Substitute any supported protocol (e.g. tinyman, pact, folks-finance, compx, dorkfi, myth-finance, haystack, reti, alpha-arcade).

curl -i https://canix402-api.compx.io/protocols/reti/opportunities

Sample response (200)

{
  "data": [
    {
      "protocol": "reti",
      "opportunityType": "staking",
      "opportunityId": "reti-staking-12",
      "assetPair": "ALGO",
      "assetIds": [
        0
      ],
      "apy": 8.5,
      "apr": 8.5,
      "yieldBasis": "apr",
      "tvlUsd": 125000.5,
      "sourceTimestamp": "2026-07-23T10:00:00.000Z",
      "fetchedAt": "2026-07-23T10:00:00.000Z",
      "entryRequirements": {
        "minAmount": {
          "assetId": 0,
          "amount": "1000000000"
        },
        "eligibilityFullyCheckable": true
      },
      "capacity": {
        "stakerSlotsRemaining": 20,
        "algoRoomMicroAlgos": "50000000000",
        "acceptingStake": true
      },
      "notes": "Réti validator-level consensus staking; quote-time eligibility checks are authoritative.",
      "executionReady": true,
      "executionShapes": [
        {
          "shapeKey": "mainnet:reti:v1:stake:algo",
          "protocol": "reti",
          "protocolVersion": "v1",
          "action": "stake",
          "variant": "algo",
          "title": "Réti ALGO stake to validator",
          "summary": "Stakes ALGO to a Réti validator via ValidatorRegistry.addStake. The registry allocates into one of the validator's pools. Builds gas ×2, ALGO payment to the registry, addStake, and optional reward-token opt-in. Enforces minEntryStake, capacity, and entry gates at quote time.",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "validatorId",
            "amount"
          ],
          "requiredAssetIds": [
            0
          ],
          "inputHints": {
            "validatorId": 12,
            "assetId": 0,
            "depositAssetId": 0
          }
        }
      ],
      "compatibleExitShapes": [
        {
          "shapeKey": "mainnet:reti:v1:unstake:algo",
          "protocol": "reti",
          "protocolVersion": "v1",
          "action": "unstake",
          "variant": "algo",
          "title": "Réti ALGO unstake from pool",
          "summary": "Removes ALGO stake from a Réti staking pool via StakingPool.removeStake. Builds gas ×2, removeStake, and optional reward-token opt-in. Partial unstake must leave at least minEntryStake (or unstake the full balance).",
          "order": 0,
          "requiredInputs": [
            "userAddress",
            "validatorId",
            "poolAppId",
            "amount"
          ],
          "requiredAssetIds": [
            0
          ],
          "inputHints": {
            "validatorId": 12,
            "assetId": 0,
            "depositAssetId": 0
          }
        }
      ]
    }
  ],
  "meta": {
    "limit": 25,
    "offset": 0,
    "includeInactive": false,
    "paymentRequired": true
  }
}

Execution shape catalog (free)

GET /execution/shapes — live catalog of verified shape keys, required inputs, and docs paths. Metadata only; does not compile quotes.

curl -s https://canix402-api.compx.io/execution/shapes | jq '.meta, (.data | length)'

Sample response (200, trimmed)

{
  "data": [
    {
      "shapeKey": "mainnet:compx:v1:borrow:asa",
      "network": "mainnet",
      "protocol": "compx",
      "protocolVersion": "v1",
      "action": "borrow",
      "variant": "asa",
      "shapeVersion": "1.0.1",
      "title": "CompX v1 ASA lending borrow",
      "description": "Borrows a base ASA from a CompX lending market against accepted LST collateral. Collateral must be in the market's on-chain accepted_collaterals set (often a cross-market LST).",
      "summary": "Borrows a base ASA from a CompX lending market against accepted LST collateral.",
      "opportunityRole": "enter",
      "supportedOpportunityTypes": [
        "lending"
      ],
      "requiredInputs": [
        "userAddress",
        "marketAppId",
        "borrowAmount",
        "collateralAmount"
      ],
      "sources": [
        {
          "kind": "sdk",
          "description": "@compx/sdk buildBorrowTransactions"
        }
      ],
      "docsPath": "protocol/docs/execution-shapes/compx-borrow-asa.md"
    },
    {
      "shapeKey": "mainnet:compx:v1:repay:asa",
      "network": "mainnet",
      "protocol": "compx",
      "protocolVersion": "v1",
      "action": "repay",
      "variant": "asa",
      "shapeVersion": "1.0.1",
      "title": "CompX v1 ASA lending repay",
      "description": "Repays outstanding base-ASA debt on a CompX lending market. App-call fees are floored to at least 2000 microAlgos after SDK simulation.",
      "summary": "Repays outstanding base-ASA debt on a CompX lending market.",
      "opportunityRole": "exit",
      "supportedOpportunityTypes": [
        "lending"
      ],
      "requiredInputs": [
        "userAddress",
        "marketAppId",
        "amount"
      ],
      "sources": [
        {
          "kind": "sdk",
          "description": "@compx/sdk buildRepayTransactions"
        }
      ],
      "docsPath": "protocol/docs/execution-shapes/compx-repay-asa.md"
    }
  ],
  "meta": {
    "paymentRequired": false,
    "shapeCount": 2,
    "note": "Catalog metadata only. Compile unsigned groups via paid POST /execution/quotes. Full catalog is larger; this sample is trimmed to CompX borrow/repay."
  }
}

Execution quotes (paid)

POST /execution/quotes — batch { quotes: [{ shapeKey, input }, ...] } (~0.1 USDC flat per request). Returns unsigned groups; Canix never signs or submits. Preflight returns 402 with PAYMENT-REQUIRED; retry with PAYMENT-SIGNATURE (same pattern as opportunities).

1) Preflight (expect 402)

curl -i https://canix402-api.compx.io/execution/quotes \
  -H 'content-type: application/json' \
  -d '{
    "quotes": [{
      "shapeKey": "mainnet:tinyman:v2:addLiquidity:flexible",
      "input": {
        "userAddress": "YOUR_ALGORAND_ADDRESS",
        "assetAId": 0,
        "assetAAmount": "1000000",
        "assetBId": 31566704,
        "assetBAmount": "1000000",
        "maxSlippageBps": 50
      }
    }]
  }'

2) Paid retry (enter liquidity)

curl -i https://canix402-api.compx.io/execution/quotes \
  -H 'content-type: application/json' \
  -H 'PAYMENT-SIGNATURE: <base64-json-payload>' \
  -d '{
    "quotes": [{
      "shapeKey": "mainnet:tinyman:v2:addLiquidity:flexible",
      "input": {
        "userAddress": "YOUR_ALGORAND_ADDRESS",
        "assetAId": 0,
        "assetAAmount": "1000000",
        "assetBId": 31566704,
        "assetBAmount": "1000000",
        "maxSlippageBps": 50
      }
    }]
  }'

3) Exit from a position (use compatibleExitShapeKeys)

After GET /positions, pick a key from compatibleExitShapeKeys and fill inputs from that row's inputHints (plus amounts you choose locally).

curl -i https://canix402-api.compx.io/execution/quotes \
  -H 'content-type: application/json' \
  -H 'PAYMENT-SIGNATURE: <base64-json-payload>' \
  -d '{
    "quotes": [{
      "shapeKey": "mainnet:tinyman:v2:removeLiquidity:multipleAssetsOut",
      "input": {
        "userAddress": "YOUR_ALGORAND_ADDRESS",
        "poolId": "1002541853",
        "assetAId": 0,
        "assetBId": 31566704,
        "poolTokenAmount": "1250000",
        "maxSlippageBps": 50
      }
    }]
  }'

Sample response (200)

{
  "data": [
    {
      "shapeKey": "mainnet:tinyman:v2:addLiquidity:flexible",
      "shapeVersion": "1.0.0",
      "network": "mainnet",
      "expiresAt": "2026-07-23T12:00:30.000Z",
      "encodedTransactions": [
        "<base64-unsigned-txn-0>",
        "<base64-unsigned-txn-1>"
      ],
      "transactions": [
        {
          "type": "axfer",
          "sender": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
          "fee": "1000",
          "groupPresent": true
        }
      ],
      "metadata": {
        "poolAddress": "TINYPQOLADDRESSPLACEHOLDERAAAAAAAAAAAAAAAAAAAAAAAAA"
      },
      "warnings": []
    }
  ],
  "meta": {
    "paymentRequired": true,
    "executionSubmitted": false,
    "quoteCount": 1,
    "note": "Unsigned groups only. Sign and submit locally; Canix never receives wallet keys."
  },
  "claimBatchRequestExample": {
    "quotes": [
      {
        "shapeKey": "mainnet:haystack:v1:claim:rewards",
        "input": {
          "userAddress": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ"
        }
      },
      {
        "shapeKey": "mainnet:compx:v1:claim:rewards",
        "input": {
          "userAddress": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ",
          "poolAppId": 123456
        }
      },
      {
        "shapeKey": "mainnet:alpha-arcade:v1:claimRewards:usdc",
        "input": {
          "userAddress": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ"
        }
      }
    ],
    "note": "Pass claimAllQuotes from GET /positions/claimable. Each quotes[] item compiles to an independent unsigned group; groups are never merged. Flat ~0.10 USDC per request."
  }
}

Wallet positions (paid)

GET /positions?address= — normalized DeFi positions with compatibleExitShapeKeys / compatibleManageShapeKeys (~0.005 USDC). Use those keys with POST /execution/quotes to exit or manage.

1) Preflight (expect 402)

curl -i 'https://canix402-api.compx.io/positions?address=YOUR_ALGORAND_ADDRESS'

2) Paid retry

curl -i 'https://canix402-api.compx.io/positions?address=YOUR_ALGORAND_ADDRESS' \
  -H 'PAYMENT-SIGNATURE: <base64-json-payload>'

Sample response (200)

{
  "data": [
    {
      "protocol": "tinyman",
      "positionType": "lp",
      "positionId": "tinyman:lp:1002541853",
      "opportunityId": "tinyman:pool:1002541853",
      "assetId": 1002541853,
      "assetSymbol": "TMPOOL2",
      "amountRaw": "1250000",
      "amount": "1.250000",
      "usdValue": 42.5,
      "compatibleExitShapeKeys": [
        "mainnet:tinyman:v2:removeLiquidity:multipleAssetsOut",
        "mainnet:tinyman:v2:removeLiquidity:singleAssetOut"
      ],
      "compatibleManageShapeKeys": [],
      "inputHints": {
        "assetAId": 0,
        "assetBId": 31566704,
        "poolId": "1002541853"
      }
    },
    {
      "protocol": "compx",
      "positionType": "debt",
      "positionId": "compx:debt:3491050310",
      "opportunityId": "compx-lending-3491050310",
      "assetId": 31566704,
      "assetSymbol": "USDC",
      "amountRaw": "250000",
      "amount": "0.250000",
      "usdValue": 0.25,
      "healthFactor": 1.8,
      "compatibleExitShapeKeys": [
        "mainnet:compx:v1:repay:asa"
      ],
      "compatibleManageShapeKeys": [],
      "notes": "Locked collateral 500000 of asset 3491050311 (LST units).",
      "inputHints": {
        "marketAppId": 3491050310,
        "assetId": 31566704
      }
    }
  ],
  "protocols": [
    {
      "protocol": "tinyman",
      "status": "ok",
      "positionCount": 1,
      "message": null
    },
    {
      "protocol": "compx",
      "status": "ok",
      "positionCount": 1,
      "message": null
    }
  ],
  "totals": {
    "suppliedUsd": 42.5,
    "borrowedUsd": 0.25,
    "rewardsUsd": 0,
    "netUsd": 42.25
  },
  "meta": {
    "address": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    "fetchedAt": "2026-08-05T12:00:00.000Z",
    "paymentRequired": true
  }
}

Claim desk (paid)

GET /positions/claimable?address= — claimable rewards with USD value, network-fee / worth-claiming hints, and claimAllQuotes ready for POST /execution/quotes (~0.001 USDC list; compile stays ~0.10 USDC flat). Groups are never merged. Agent loop: positions (optional) → claimable → quote → local sign.

1) Preflight (expect 402)

curl -i 'https://canix402-api.compx.io/positions/claimable?address=YOUR_ALGORAND_ADDRESS'

2) Paid retry

curl -i 'https://canix402-api.compx.io/positions/claimable?address=YOUR_ALGORAND_ADDRESS' \
  -H 'PAYMENT-SIGNATURE: <base64-json-payload>'

3) Compile claim-all (use claimAllQuotes)

curl -i https://canix402-api.compx.io/execution/quotes \
  -H 'content-type: application/json' \
  -H 'PAYMENT-SIGNATURE: <base64-json-payload>' \
  -d '{
    "quotes": [
      {
        "shapeKey": "mainnet:haystack:v1:claim:rewards",
        "input": { "userAddress": "YOUR_ALGORAND_ADDRESS" }
      },
      {
        "shapeKey": "mainnet:compx:v1:claim:rewards",
        "input": { "userAddress": "YOUR_ALGORAND_ADDRESS", "poolAppId": 123456 }
      }
    ]
  }'

Sample response (200)

{
  "data": [
    {
      "protocol": "haystack",
      "positionId": "haystack:reward:3321763884:usdc",
      "opportunityId": "haystack-staking-hay",
      "positionType": "reward",
      "assetId": 31566704,
      "assetSymbol": "USDC",
      "amountRaw": "1500000",
      "amount": "1.500000",
      "usdValue": 1.5,
      "claimKey": "haystack:claim:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ",
      "compatibleClaimShapeKeys": [
        "mainnet:haystack:v1:claim:rewards"
      ],
      "quote": {
        "shapeKey": "mainnet:haystack:v1:claim:rewards",
        "input": {
          "userAddress": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ"
        }
      },
      "estimatedNetworkFeeMicroAlgos": "5000",
      "estimatedNetworkFeeUsd": 0.001,
      "worthClaiming": true,
      "caveats": [
        "Pending USDC from staker box; live accrual may be higher until the next drip/claim."
      ]
    },
    {
      "protocol": "haystack",
      "positionId": "haystack:reward:3321763884:hay",
      "opportunityId": "haystack-staking-hay",
      "positionType": "reward",
      "assetId": 3160000000,
      "assetSymbol": "HAY",
      "amountRaw": "25000000",
      "amount": "25.000000",
      "usdValue": 0.5,
      "claimKey": "haystack:claim:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ",
      "compatibleClaimShapeKeys": [
        "mainnet:haystack:v1:claim:rewards"
      ],
      "quote": {
        "shapeKey": "mainnet:haystack:v1:claim:rewards",
        "input": {
          "userAddress": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ"
        }
      },
      "estimatedNetworkFeeMicroAlgos": "5000",
      "estimatedNetworkFeeUsd": 0.001,
      "worthClaiming": true
    },
    {
      "protocol": "compx",
      "positionId": "compx:reward:123456:31566704",
      "opportunityId": "compx-staking-123456",
      "positionType": "reward",
      "assetId": 31566704,
      "assetSymbol": "USDC",
      "amountRaw": "500000",
      "amount": "0.500000",
      "usdValue": 0.5,
      "claimKey": "compx:claim:123456",
      "compatibleClaimShapeKeys": [
        "mainnet:compx:v1:claim:rewards"
      ],
      "quote": {
        "shapeKey": "mainnet:compx:v1:claim:rewards",
        "input": {
          "userAddress": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ",
          "poolAppId": 123456
        }
      },
      "estimatedNetworkFeeMicroAlgos": "250000",
      "estimatedNetworkFeeUsd": 0.05,
      "worthClaiming": true,
      "inputHints": {
        "poolAppId": 123456,
        "assetId": 31566704
      }
    },
    {
      "protocol": "tinyman",
      "positionId": "tinyman:staked:stalgo:dummy",
      "opportunityId": "tinyman-staking-stalgo",
      "positionType": "staked",
      "assetId": 0,
      "assetSymbol": "stALGO",
      "amountRaw": "1000000",
      "amount": "1.000000",
      "usdValue": null,
      "claimKey": "tinyman:stalgo-claim:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ",
      "compatibleClaimShapeKeys": [
        "mainnet:tinyman:restake-v1:claimRewards:stAlgo"
      ],
      "quote": {
        "shapeKey": "mainnet:tinyman:restake-v1:claimRewards:stAlgo",
        "input": {
          "userAddress": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ"
        }
      },
      "estimatedNetworkFeeMicroAlgos": "2000",
      "estimatedNetworkFeeUsd": 0.0004,
      "worthClaiming": null,
      "caveats": [
        "Pending TINY restake rewards are not collected on positions; claim via manage shape."
      ]
    }
  ],
  "protocols": [
    {
      "protocol": "tinyman",
      "status": "ok",
      "positionCount": 1,
      "message": null
    },
    {
      "protocol": "compx",
      "status": "ok",
      "positionCount": 1,
      "message": null
    },
    {
      "protocol": "pact",
      "status": "ok",
      "positionCount": 0,
      "message": null
    },
    {
      "protocol": "haystack",
      "status": "ok",
      "positionCount": 2,
      "message": null
    },
    {
      "protocol": "alpha-arcade",
      "status": "ok",
      "positionCount": 0,
      "message": null
    }
  ],
  "totals": {
    "claimableUsd": null,
    "estimatedNetworkFeeUsd": 0.0514,
    "worthClaimingUsd": null
  },
  "claimAllQuotes": {
    "quotes": [
      {
        "shapeKey": "mainnet:haystack:v1:claim:rewards",
        "input": {
          "userAddress": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ"
        }
      },
      {
        "shapeKey": "mainnet:compx:v1:claim:rewards",
        "input": {
          "userAddress": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ",
          "poolAppId": 123456
        }
      },
      {
        "shapeKey": "mainnet:tinyman:restake-v1:claimRewards:stAlgo",
        "input": {
          "userAddress": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ"
        }
      }
    ]
  },
  "meta": {
    "address": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ",
    "fetchedAt": "2026-08-13T12:00:00.000Z",
    "algoUsd": 0.2,
    "paymentRequired": true
  }
}

Haystack swaps (quote → opt-in → paid transactions)

Walletless swap flow. Amounts are integer asset base units. Free quote and opt-in; paid POST /swaps/transactions (~0.005 USDC). Canix never signs or submits.

1) Free quote

curl -i https://canix402-api.compx.io/swaps/quote \
  -H 'content-type: application/json' \
  -d '{
    "address": "YOUR_ALGORAND_ADDRESS",
    "fromAssetId": 0,
    "toAssetId": 31566704,
    "amount": "1000000",
    "type": "fixed-input"
  }'

Sample quote response (200)

{
  "data": {
    "address": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
    "fromAssetId": "0",
    "toAssetId": "31566704",
    "amount": "1000000",
    "type": "fixed-input",
    "quotedAmount": "185000",
    "createdAt": "2026-07-30T12:00:00.000Z",
    "expiresAt": "2026-07-30T12:02:00.000Z",
    "priceBaseline": 0.185,
    "usdIn": 0.2,
    "usdOut": 0.185,
    "requiredAppOptIns": [
      1234567890
    ],
    "route": [
      {
        "protocol": "tinyman",
        "poolId": "1002541853"
      }
    ],
    "quotes": [],
    "protocolFees": [],
    "txnPayload": "<opaque-haystack-quote-blob>",
    "note": "Pass this quote object unchanged to /swaps/optin and /swaps/transactions. Refresh after any opt-in submit."
  },
  "meta": {
    "paymentRequired": false,
    "executionSubmitted": false
  }
}

2) Free opt-in (pass quote unchanged)

curl -i https://canix402-api.compx.io/swaps/optin \
  -H 'content-type: application/json' \
  -d '{
    "address": "YOUR_ALGORAND_ADDRESS",
    "quote": { /* full data object from /swaps/quote */ }
  }'

Sample opt-in response (200)

{
  "data": {
    "required": true,
    "transactions": [
      {
        "type": "axfer",
        "note": "asset opt-in for toAssetId",
        "encoded": "<base64-unsigned-txn-0>"
      },
      {
        "type": "appl",
        "note": "application opt-in for requiredAppOptIns[0]",
        "encoded": "<base64-unsigned-txn-1>"
      }
    ],
    "userSignIndexes": [
      0,
      1
    ],
    "createdAt": "2026-07-30T12:00:05.000Z",
    "expiresAt": "2026-07-30T12:02:00.000Z"
  },
  "meta": {
    "paymentRequired": false,
    "executionSubmitted": false,
    "note": "Empty transactions[] means no opt-ins are missing. Sign and submit locally, then fetch a fresh quote before /swaps/transactions."
  }
}

If transactions is non-empty, sign and submit locally, wait for confirmation, then fetch a fresh quote before the paid step.

3) Paid transactions — preflight (expect 402)

curl -i https://canix402-api.compx.io/swaps/transactions \
  -H 'content-type: application/json' \
  -d '{
    "address": "YOUR_ALGORAND_ADDRESS",
    "quote": { /* fresh quote data object */ },
    "slippage": 0.005
  }'

4) Paid transactions — retry with PAYMENT-SIGNATURE

curl -i https://canix402-api.compx.io/swaps/transactions \
  -H 'content-type: application/json' \
  -H 'PAYMENT-SIGNATURE: <base64-json-payload>' \
  -d '{
    "address": "YOUR_ALGORAND_ADDRESS",
    "quote": { /* fresh quote data object */ },
    "slippage": 0.005
  }'

Sample transactions response (200)

{
  "data": {
    "encodedTransactions": [
      "<base64-txn-0-user-sign>",
      "<base64-txn-1-haystack-presigned>",
      "<base64-txn-2-user-sign>"
    ],
    "userSignIndexes": [
      0,
      2
    ],
    "createdAt": "2026-07-30T12:00:30.000Z",
    "expiresAt": "2026-07-30T12:02:00.000Z",
    "note": "Sign only userSignIndexes members. Preserve Haystack pre-signed members and atomic group order. Submit the complete group via your own Algod client."
  },
  "meta": {
    "paymentRequired": true,
    "executionSubmitted": false
  }
}

Sign only userSignIndexes, keep Haystack pre-signed members and group order, then submit the complete atomic group through your own Algod client.

Error responses

Paid routes return 402 before payment; validation and server errors use a stable envelope.

402 — payment required

{
  "error": {
    "code": "MISSING_PAYMENT_SIGNATURE",
    "message": "The paid endpoint was called without PAYMENT-SIGNATURE."
  }
}

400 — invalid Algorand address (personalized route)

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Query parameter 'address' is not a valid Algorand address."
  }
}