Examples
All examples use the public Caddy gateway URL. Replace placeholders before running paid retries.
An agent may use Algorand, Base, or both. Algorand examples below pass an Algorand address only.
Omitting a Base address does not change rank, eligibility, price, or access. A Base
0x address appears only on the Morpho quote.
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 selects an accept by network and
builds that payload. Algorand is first and a complete payment on its own. Base is the same
price and is optional. Base64-encode the JSON as PAYMENT-SIGNATURE.
Algorand
{
"x402Version": 2,
"scheme": "exact",
"network": "algorand-mainnet",
"accepted": { /* selected Algorand accept from PAYMENT-REQUIRED */ },
"payload": {
"paymentGroup": ["<base64-signed-txn>"],
"paymentIndex": 0
},
"paymentRequired": { /* decoded PAYMENT-REQUIRED */ }
} Base
{
"x402Version": 2,
"scheme": "exact",
"network": "eip155:8453",
"accepted": { /* selected Base accept from PAYMENT-REQUIRED */ },
"payload": {
"signature": "0x<65-byte EIP-712 signature>",
"authorization": {
"from": "0x<payer>",
"to": "0x<payTo>",
"value": "10000",
"validAfter": "<unix seconds>",
"validBefore": "<unix seconds>",
"nonce": "0x<32 bytes>"
}
},
"paymentRequired": { /* decoded PAYMENT-REQUIRED */ }
} Sample response payloads
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.",
"risk": {
"confidence": "low",
"sourceAgeSeconds": 5097600,
"volatilityBucket": "unknown"
},
"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.",
"risk": {
"utilization": 4.4,
"liquidationThreshold": 80,
"ltv": 70,
"borrowApr": 8.5,
"confidence": "low",
"sourceAgeSeconds": 4283377
},
"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"
}
} Opportunity history series
GET /opportunities/:id/history — bounded APY/TVL snapshots for
window=1d|7d|30d (default 30d). Empty until the snapshot job has run.
Includes a stability signal so snapshot APY cannot dominate plan sizing.
curl -i 'https://canix402-api.compx.io/opportunities/tinyman:pool:1002541853/history?window=30d' Sample response (200)
{
"data": {
"opportunityId": "tinyman:pool:1002541853",
"window": "30d",
"points": [
{
"ts": "2026-08-04T12:00:00.000Z",
"apy": 12.1,
"tvlUsd": 2400000
},
{
"ts": "2026-08-05T12:00:00.000Z",
"apy": 12.4,
"tvlUsd": 2425000
},
{
"ts": "2026-08-06T12:00:00.000Z",
"apy": 12.5,
"tvlUsd": 2450000.5
}
],
"stability": {
"bucket": "high",
"sampleCount": 3,
"apyMean": 12.333333,
"apyStdev": 0.208167
}
},
"meta": {
"paymentRequired": true,
"windowSeconds": 2592000,
"snapshotRetentionDays": 30,
"snapshotCount": 3
}
} 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, live multi-router
opt-in → swap compose when requiredAssetIds differ from the budget asset,
protocol setup chains, and enter quotes as independent unsigned groups (never merged).
Reuses quotes[] / order / prerequisiteShapeKeys.
Attaches data.simulation (predicted deltas, fail-closed reasons) when groups
are compiled. 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,
"tvlUsd": 1000000,
"sourceTimestamp": "2026-08-16T10:00:00.000Z",
"executionReady": true,
"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": [],
"simulation": {
"wouldSucceed": true,
"reasons": [],
"balanceDeltas": [
{
"address": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"assetId": 0,
"before": "5000000",
"after": "3999000",
"delta": "-1001000"
}
],
"expectedPositionDelta": {
"summary": "Enter 1000000 of asset 0 on reti-staking-12 (reti).",
"entries": [
{
"opportunityId": "reti-staking-12",
"protocol": "reti",
"assetId": 0,
"amount": "1000000",
"action": "enter"
}
]
},
"groups": [
{
"index": 0,
"shapeKey": "mainnet:reti:v1:stake:algo",
"opportunityId": "reti-staking-12",
"wouldSucceed": true,
"reasons": [],
"balanceDeltas": [
{
"address": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"assetId": 0,
"before": "5000000",
"after": "3999000",
"delta": "-1001000"
}
],
"expectedPositionDelta": {
"summary": "Enter 1000000 of asset 0 on reti-staking-12 (reti).",
"entries": [
{
"opportunityId": "reti-staking-12",
"protocol": "reti",
"assetId": 0,
"amount": "1000000",
"action": "enter"
}
]
}
}
],
"signed": false,
"submitted": false
}
},
"meta": {
"address": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"budget": {
"assetId": 0,
"amount": "1000000"
},
"fetchedAt": "2026-08-16T10:00:00.000Z",
"paymentRequired": true,
"executionSubmitted": false,
"quoteTimeAuthoritative": true,
"eligibilityEndpoint": "/eligibility"
}
} Swap-aware enter compose (paid)
POST /execution/compose — ~0.10 USDC. State “I hold asset A, I want this
opportunity.” Returns sequenced unsigned groups: opt-in → winning swap → enter, driven by
requiredAssetIds. Groups are never merged. Signer indexes and pre-signed
members are preserved; sign only user legs and submit locally. Failure modes (stale quote,
missing opt-in, slippage) appear on step warnings. Prefer POST /plans for budget
allocation.
curl -i https://canix402-api.compx.io/execution/compose -H 'content-type: application/json' -d '{"address":"YOUR_ALGORAND_ADDRESS","opportunityId":"reti-staking-12","fromAssetId":1,"amount":"1000000","slippage":1}' Sample response (200)
{
"data": {
"opportunityId": "reti-staking-12",
"protocol": "reti",
"opportunityType": "staking",
"assetPair": "ALGO",
"fromAssetId": 1,
"toAssetId": 0,
"inputAmount": "1000000",
"enterAmount": "247500",
"slippage": 1,
"steps": [
{
"kind": "eligibility",
"order": 0,
"compileStatus": "compiled",
"warnings": [
"Eligibility is gated on missing required assets; swap-aware compose unblocks enter after the swap group confirms."
]
},
{
"kind": "opt-in",
"order": 1,
"compileStatus": "compiled",
"shapeKey": "mainnet:swap:router:optin:required",
"quote": {
"shapeKey": "mainnet:swap:router:optin:required",
"encodedTransactions": [
"<base64-unsigned-opt-in>"
],
"userSignIndexes": [
0
],
"groupTransactions": [
{
"index": 0,
"signer": "user",
"encodedTransaction": "<base64-unsigned-opt-in>"
}
]
},
"warnings": [
"Missing opt-in: submit the opt-in group and wait for confirmation before submitting the swap group."
]
},
{
"kind": "swap",
"order": 2,
"compileStatus": "compiled",
"shapeKey": "mainnet:swap:router:swap:fixed-input",
"prerequisiteShapeKeys": [
"mainnet:swap:router:optin:required"
],
"quote": {
"shapeKey": "mainnet:swap:router:swap:fixed-input",
"encodedTransactions": [
"<base64-user-leg>"
],
"userSignIndexes": [
0
],
"groupTransactions": [
{
"index": 0,
"signer": "user",
"encodedTransaction": "<base64-user-leg>"
},
{
"index": 1,
"signer": "logicsig",
"encodedTransaction": "<base64-router-leg>",
"signedTransaction": "<base64-presigned>"
}
]
},
"warnings": [
"Stale quote: submit the swap group before quote.expiresAt.",
"Slippage: enter is compiled with a haircut of quotedAmount; re-quote enter if the confirmed swap delivers less."
]
},
{
"kind": "enter",
"order": 3,
"compileStatus": "compiled",
"shapeKey": "mainnet:reti:v1:stake:algo",
"prerequisiteShapeKeys": [
"mainnet:swap:router:optin:required",
"mainnet:swap:router:swap:fixed-input"
]
}
],
"quotes": [
{
"shapeKey": "mainnet:reti:v1:stake:algo",
"input": {
"userAddress": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"amount": "247500"
}
}
],
"warnings": [
"Groups stay unsigned and unmerged. Sign only user legs; preserve any pre-signed members."
]
},
"meta": {
"paymentRequired": true,
"executionSubmitted": false,
"groupsMerged": false
}
} Simulate / expected delta (paid)
POST /execution/simulate — ~0.10 USDC. Pass compiled unsigned
groups[] from a plan or quote. Returns predicted balance and position deltas
without signing or submitting. Fail closed with machine-readable
reasons[].code (stale-quote, not-opted-in,
min-balance, health-factor-too-low, capacity).
POST /plans already attaches data.simulation when groups are compiled.
Canix never signs or submits.
curl -i https://canix402-api.compx.io/execution/simulate -H 'content-type: application/json' -d '{"address":"YOUR_ALGORAND_ADDRESS","groups":[{"shapeKey":"mainnet:reti:v1:stake:algo","encodedTransactions":["<base64-unsigned-txn>"]}]}' Sample response (200)
{
"data": {
"wouldSucceed": true,
"reasons": [],
"balanceDeltas": [
{
"address": "RS7TLLQRXKBAQDAVTSZC2ZLMVMLNSCL3FOUOESJJZ5XSKFFL56UI6X33CI",
"assetId": 0,
"before": "5000000",
"after": "3999000",
"delta": "-1001000"
}
],
"expectedPositionDelta": {
"summary": "Enter 1000000 of asset 0 on reti-staking-12 (reti).",
"entries": [
{
"opportunityId": "reti-staking-12",
"protocol": "reti",
"assetId": 0,
"amount": "1000000",
"action": "enter"
}
]
},
"groups": [
{
"index": 0,
"shapeKey": "mainnet:reti:v1:stake:algo",
"opportunityId": "reti-staking-12",
"wouldSucceed": true,
"reasons": [],
"balanceDeltas": [
{
"address": "RS7TLLQRXKBAQDAVTSZC2ZLMVMLNSCL3FOUOESJJZ5XSKFFL56UI6X33CI",
"assetId": 0,
"before": "5000000",
"after": "3999000",
"delta": "-1001000"
}
],
"expectedPositionDelta": {
"summary": "Enter 1000000 of asset 0 on reti-staking-12 (reti).",
"entries": [
{
"opportunityId": "reti-staking-12",
"protocol": "reti",
"assetId": 0,
"amount": "1000000",
"action": "enter"
}
]
}
}
],
"signed": false,
"submitted": false
},
"meta": {
"address": "RS7TLLQRXKBAQDAVTSZC2ZLMVMLNSCL3FOUOESJJZ5XSKFFL56UI6X33CI",
"fetchedAt": "2026-08-21T12:00:00.000Z",
"paymentRequired": true,
"executionSubmitted": false,
"signed": false
}
} Policy-as-a-service (paid)
POST /policy/validate — ~0.25 USDC compiler SKU. Pass a compiled
plan or proposed quotes[] plus an operator policy document
(schemaVersion: "1.0.0"). Returns machine-readable
pass and reasons[] for per-protocol weight, ALGO reserve floor,
TVL/freshness floors, no-new-borrows, and execution-ready only. Reuses fields
already on the compiled object and fails closed when a required field is missing
— Canix does not re-quote on-chain, sign, or submit. Copy
protocol/docs/policy-brownie.sample.json.
curl -i https://canix402-api.compx.io/policy/validate -H 'content-type: application/json' -d '{"policy":{"schemaVersion":"1.0.0","maxProtocolWeightBps":4000,"minAlgoReserveMicroAlgos":"1000000","minTvlUsd":25000,"maxSourceAgeSeconds":86400,"noNewBorrows":true,"executionReadyOnly":true},"quotes":[{"shapeKey":"mainnet:reti:v1:stake:algo","protocol":"reti","opportunityId":"reti-staking-12","weightBps":4000,"allocatedAmount":"1000000","allocatedAssetId":0,"tvlUsd":1000000,"sourceTimestamp":"2026-08-28T11:00:00.000Z","executionReady":true}],"walletAlgoMicroAlgos":"5000000"}' Sample response (200)
{
"data": {
"pass": true,
"reasons": [],
"policySchemaVersion": "1.0.0",
"evaluatedAt": "2026-08-28T12:00:00.000Z",
"signed": false,
"submitted": false
},
"meta": {
"fetchedAt": "2026-08-28T12:00:00.000Z",
"paymentRequired": true,
"executionSubmitted": false,
"signed": false,
"quoteTimeAuthoritative": true
}
} 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, stamm).
AlgoFi and Humble LP holdings appear on GET /positions only.
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. meta.caveatsDocsPath points at
protocol-specific construction caveats so agents do not guess pool discovery, opt-ins,
min-balance, slippage, or app upgrades.
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. Protocol construction caveats: protocol/docs/execution-shapes/protocol-caveats.md. Full catalog is larger; this sample is trimmed to CompX borrow/repay.",
"caveatsDocsPath": "protocol/docs/execution-shapes/protocol-caveats.md"
}
} 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
}
}]
}' 4) Morpho vault deposit on Base (optional)
Use this only when the agent is acting on Base. userAddress is a Base
0x address. The response is unsigned calldata (encodedTransactions hex
and transactions[].evmCall). An Algorand-only agent can skip this call. Pay the
same 0.1 USDC on either rail. The payment signature is not the vault calldata.
curl -i https://canix402-api.compx.io/execution/quotes \
-H 'content-type: application/json' \
-H 'PAYMENT-SIGNATURE: <base64-json-payload>' \
-d '{
"quotes": [{
"shapeKey": "base:morpho:vault:deposit:erc4626",
"input": {
"userAddress": "0xYOUR_BASE_ADDRESS",
"vaultAddress": "0xef417a2512C5a41f69AE4e021648b69a7CdE5D03",
"amount": "1000000"
}
}]
}' 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."
}
} Haystack Launch
Bonding-curve launches are separate from yield positions. List tokens still bonding, read one
token free, then compile a launch or a bonding buy with POST /execution/quotes
(0.10 USDC flat). A bonding buy is not POST /swaps/transactions. Pass an
ipfs:// or https:// image URL you already have. Holdings while a
token is bonding are virtual and are not wallet ASAs.
1) List tokens still bonding (paid, 0.01 USDC)
GET /protocols/haystack/launches. The default window is the last 60 days.
q matches name or ticker. minProgress and maxProgress are
inclusive bonding percents. order is asc or desc (default
desc). A research session can pay this route.
curl -i 'https://canix402-api.compx.io/protocols/haystack/launches?q=len&minProgress=10&maxProgress=90&order=desc&limit=25' Sample list (200, trimmed)
{
"data": {
"launchedAfter": "2026-08-06T21:50:00.000Z",
"launchedBefore": "2026-10-05T21:50:00.000Z",
"order": "desc",
"total": 1,
"launches": [
{
"tokenNum": 175,
"assetId": 3729195158,
"name": "LENIN",
"symbol": "LENIN",
"assetUrl": "ipfs://bafkreiexample",
"creator": "LOLJKAN3NMRMAWAPYKQOEJPVM37KKZCMM7DMGOU2CWN45RKSDZB6QAKSMI",
"bondingTokenId": 0,
"launchedAt": "2026-10-02T22:56:32.000Z",
"progressPercent": 12.5,
"priceBonding": "0.000001",
"priceUsd": "0.000002",
"realTokenReserves": "700000000000000",
"realBondingReserves": "5000000",
"initialRealTokenReserves": "800000000000000",
"bondingTargetUsd": "2500000000",
"priceMultiplier": "20",
"buyShapeKey": "mainnet:haystack:v1:buy:bonding"
}
]
},
"meta": {
"paymentRequired": true,
"shapeKey": "mainnet:haystack:v1:buy:bonding"
}
} 2) One token (free)
GET /protocols/haystack/launches/{tokenNum}. After the first buy,
?assetId= resolves the token. ?address= adds virtual
userHolding. bondingOn 0 is still on the curve. A
graduated token sets buyShapeKey to null and points note at
POST /swaps/quote.
curl -s https://canix402-api.compx.io/protocols/haystack/launches/175 | jq
curl -s 'https://canix402-api.compx.io/protocols/haystack/launches/0?assetId=3729195158&address=YOUR_ALGORAND_ADDRESS' | jq Sample status (200, trimmed)
{
"data": {
"tokenNum": 175,
"assetId": 3729195158,
"name": "LENIN",
"symbol": "LENIN",
"assetUrl": "ipfs://bafkreiexample",
"description": "",
"socialWebsite": "",
"socialX": "",
"socialTelegram": "",
"socialDiscord": "",
"creator": "LOLJKAN3NMRMAWAPYKQOEJPVM37KKZCMM7DMGOU2CWN45RKSDZB6QAKSMI",
"bondingTokenId": 0,
"bondingOn": 0,
"phase": "bonding",
"progressPercent": 12.5,
"priceBonding": "0.000001",
"priceUsd": "0.000002",
"realTokenReserves": "700000000000000",
"realBondingReserves": "5000000",
"initialRealTokenReserves": "800000000000000",
"virtualTokenReserves": "1000000000000000",
"virtualBondingReserves": "1000000000",
"bondingTargetUsd": "2500000000",
"priceMultiplier": "20",
"poolAppId": 0,
"lpTokenId": 0,
"buyShapeKey": "mainnet:haystack:v1:buy:bonding",
"userHolding": null,
"note": null
},
"meta": {
"paymentRequired": false
}
} 3) Launch a token (paid execution quote)
Shape mainnet:haystack:v1:launch:token. bondingTokenId 0 is ALGO. Optional initialBuyAmount is bonding-asset base units. A
first buy that would finish the curve is rejected.
curl -i https://canix402-api.compx.io/execution/quotes \
-H 'content-type: application/json' \
-d '{
"quotes": [{
"shapeKey": "mainnet:haystack:v1:launch:token",
"input": {
"userAddress": "YOUR_ALGORAND_ADDRESS",
"symbol": "LENIN",
"name": "Lenin",
"assetUrl": "ipfs://bafkreiexample",
"bondingTokenId": 0,
"initialBuyAmount": "1000000"
}
}]
}' 4) Buy a token still bonding (paid execution quote)
Shape mainnet:haystack:v1:buy:bonding. Pay the bonding asset directly, or pass
another fromAssetId and the Haystack router supplies it in the same group. A
routed buy that would take 90% or more of the remaining real reserves is refused; pay in the
bonding asset for that size. slippageBps 50 is 0.50%.
# Direct ALGO buy (bondingTokenId 0)
curl -i https://canix402-api.compx.io/execution/quotes \
-H 'content-type: application/json' \
-d '{
"quotes": [{
"shapeKey": "mainnet:haystack:v1:buy:bonding",
"input": {
"userAddress": "YOUR_ALGORAND_ADDRESS",
"tokenNum": 175,
"fromAssetId": 0,
"amount": "1000000",
"slippageBps": 50
}
}]
}'
# Routed from USDC (31566704) through the Haystack router
curl -i https://canix402-api.compx.io/execution/quotes \
-H 'content-type: application/json' \
-d '{
"quotes": [{
"shapeKey": "mainnet:haystack:v1:buy:bonding",
"input": {
"userAddress": "YOUR_ALGORAND_ADDRESS",
"assetId": 3729195158,
"fromAssetId": 31566704,
"amount": "1000000",
"slippageBps": 50
}
}]
}' 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
}
} Swaps (quote → opt-in → paid transactions)
Walletless multi-router 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": {
"router": "hogswap",
"address": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"fromAssetId": "0",
"toAssetId": "31566704",
"amount": "1000000",
"type": "fixed-input",
"quotedAmount": "185000",
"minOut": "183150",
"networkFeeMicroAlgos": "5000",
"slippageBps": 100,
"createdAt": "2026-07-30T12:00:00.000Z",
"expiresAt": "2026-07-30T12:00:30.000Z",
"score": {
"expectedNetOut": "185000",
"minOut": "183150",
"expectedIn": "1000000",
"maxIn": "1000000",
"networkFeeMicroAlgos": "5000",
"feeAlreadyNetted": true
},
"alternatives": [
{
"router": "haystack",
"status": "quoted",
"expectedNetOut": "180000",
"minOut": "178200",
"networkFeeMicroAlgos": "0"
},
{
"router": "hogswap",
"status": "quoted",
"expectedNetOut": "185000",
"minOut": "183150",
"networkFeeMicroAlgos": "5000"
}
],
"legs": [
{
"protocol": "stamm",
"poolId": "1002541853"
}
],
"payload": {
"note": "opaque winner quote; pass unchanged"
},
"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": {
"router": "hogswap",
"transactions": [
{
"index": 0,
"encodedTransaction": "<base64-txn-0-user-sign>",
"signer": "user"
},
{
"index": 1,
"encodedTransaction": "<base64-txn-1-protocol>",
"signer": "protocol"
},
{
"index": 2,
"encodedTransaction": "<base64-txn-2-user-sign>",
"signer": "user"
}
],
"userSignIndexes": [
0,
2
],
"createdAt": "2026-07-30T12:00:30.000Z",
"quoteExpiresAt": "2026-07-30T12:00:30.000Z",
"note": "Sign only userSignIndexes members. Preserve 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 any pre-signed members and group order, then
submit the complete atomic group through your own Algod client.
Prepaid agent sessions
One POST /sessions (~0.25 USDC) mints a walletless receipt that unlocks N research
calls and M quotes/plans until TTL. Send X-Canix-Session on session-eligible routes
instead of spraying tiny one-shots. Create/refresh stay one-shot only. Fail-closed on expiry
or exhausted quota — omit the header to fall back to per-request payment.
1) Buy a session (expect 402, then retry with PAYMENT-SIGNATURE)
curl -i https://canix402-api.compx.io/sessions -X POST -H 'content-type: application/json' -d '{}' 2) Read remaining N/M (free)
curl -s https://canix402-api.compx.io/sessions/csess_YOUR_SESSION_ID | jq '.data.remaining' 3) Consume research with the receipt
curl -i 'https://canix402-api.compx.io/opportunities' -H 'X-Canix-Session: csess_YOUR_SESSION_ID' Sample receipt (200)
{
"data": {
"uri": "canix://session/csess_demo",
"sessionId": "csess_demo",
"createdAt": "2026-08-24T12:00:00.000Z",
"expiresAt": "2026-08-24T16:00:00.000Z",
"ttlSeconds": 14400,
"budget": {
"research": 50,
"quotes": 10
},
"remaining": {
"research": 49,
"quotes": 10
},
"consumed": {
"research": 1,
"quotes": 0
},
"status": "active"
},
"meta": {
"paymentRequired": false,
"access": "receipt",
"receiptUri": "canix://session/csess_demo"
}
} Watch / webhook retainers
One POST /watch (~0.25 USDC) registers a walletless address + thresholds retainer
instead of polling /positions. Optional HTTPS webhook; firings are HMAC-signed
(X-Canix-Signature) and idempotent (X-Canix-Idempotency-Key). Canix
never stores wallet keys. No webhook means MCP-only: read firings from the free receipt.
1) Register a watch (expect 402, then retry with PAYMENT-SIGNATURE)
curl -i https://canix402-api.compx.io/watch -X POST -H 'content-type: application/json' -d '{"address":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ","thresholds":{"healthFactor":1.25,"claimableUsd":8},"webhookUrl":"https://hooks.example/canix"}' 2) Read recent firings (free; secret never returned)
curl -s https://canix402-api.compx.io/watch/cwatch_YOUR_WATCH_ID | jq '.data.firings' 3) Rotate the HMAC secret (free; send the current secret)
curl -i https://canix402-api.compx.io/watch/cwatch_YOUR_WATCH_ID/rotate-secret -X POST -H 'X-Canix-Watch-Secret: wsec_YOUR_CURRENT_SECRET' Sample receipt (200)
{
"data": {
"uri": "canix://watch/cwatch_demo",
"watchId": "cwatch_demo",
"address": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ",
"thresholds": {
"healthFactor": 1.25,
"claimableUsd": 8,
"apyDropBps": 100,
"retiCapacity": true
},
"webhookUrl": "https://hooks.example/canix",
"createdAt": "2026-09-03T12:00:00.000Z",
"expiresAt": "2026-09-04T12:00:00.000Z",
"ttlSeconds": 86400,
"status": "active",
"firings": [
{
"firingId": "wfire_demo",
"idempotencyKey": "wfire_cwatch_demo_healthFactor_wep_demo",
"kind": "healthFactor",
"firedAt": "2026-09-03T12:05:00.000Z",
"threshold": 1.25,
"previous": 1.4,
"current": 1.1,
"delivered": true,
"deliveryStatus": "delivered"
}
]
},
"meta": {
"paymentRequired": false,
"access": "receipt",
"receiptUri": "canix://watch/cwatch_demo",
"secretShown": false
}
} 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."
}
}