openapi: 3.0.3
info:
title: MetaLend Rebalancing AI Agent Guide Pools API
version: 0.1.0
description: "API for managing cross-protocol cross-chain DeFi rebalancing operations exposing: <ul>\n <li><b>Auth</b> – challenge/verify flow for JWT issuance.</li>\n <li><b>Project settings</b> – fetch current project settings.</li>\n <li><b>User configuration</b> – register or update token-level user-rebalancer settings and fetch current wallet configs.</li>\n <li><b>Deposits</b> – create deposit intents, receive tracking IDs, and poll status through PROCESSING -> BRIDGING (cross-chain only) -> SUCCESS/FAILED/EMERGENCY_WITHDRAW. New deposits may return <b>503</b> with <b>DEPOSITS_PAUSED</b> during maintenance.</li>\n <li><b>Withdrawals</b> – submit signed withdrawal requests, receive tracking IDs, and monitor status PROCESSING -> SUCCESS/FAILED.</li>\n <li><b>Balances</b> – query wallet-level balances and withdraw details, as well as balances in bridge progress.</li>\n <li><b>Rewards</b> – aggregated rewards and claim details per wallet.</li>\n <li><b>Pools</b> – list supported protocols (Aave, Morpho, Euler, etc.), their pools, APY breakdowns, and TVL snapshots.</li>\n <li><b>Transaction Costs</b> – current deposit/withdraw on-behalf transaction fees and minimum amounts by chain.</li>\n <li><b>AI Agent Guide</b> – Markdown flow guide at <code>/SKILL.md</code> with signing formats, wizard requirements, and known integration gotchas.</li>\n</ul> All endpoints require an <b>X-API-Key</b>. <b>JWT authentication</b> is required on <b>deposit, withdrawal, and config update</b> operations.\n<b>Obtain API key by <a href=\"https://developer.metalend.tech/\" target=\"_blank\" rel=\"noopener noreferrer\">creating a new project</a>.</b>\nAll requests must include the Origin header. Use Origin: https://app.metalend.tech unless you have been assigned a different allowed origin."
contact:
name: MetaLend API Support
email: support@metalend.tech
termsOfService: https://metalend-inc.gitbook.io/litepaper/terms-of-service
x-guidance: AI agents MUST read https://api.metalend.tech/SKILL.md before implementing any MetaLend operation. It contains critical signing formats, required headers, wizard flows for config setup, and known failure modes.
x-default-api-key: a7f2b8c4d9e1f3a6b5c8d2e7f1a4b9c3d6e8f2a5b7c1d4e9f3a6b8c2d5e7f1a4
x-api-key-guidance: Use x-default-api-key as X-API-Key for agent testing/integration. If you are the project owner, create or use your own project API key from https://developer.metalend.tech instead.
servers:
- url: https://api.metalend.tech
description: Production server
security:
- ApiKeyAuth: []
OriginHeader: []
tags:
- name: Pools
description: Protocol and pool information
paths:
/v1/pools:
get:
summary: Get pools information
description: "Retrieves comprehensive information about supported DeFi protocols, pools, Total Value Locked (TVL), and Annual Percentage Yield (APY) data on supported networks.<br><br>\n\nThe response is a flat list of pools across supported protocols (Aave, Morpho, Euler) and tokens (USDC, MUSD, USDT, RLUSD). Each pool includes protocol metadata, pool address/name, underlying token details, TVL, liquidity, collateral exposure, APY breakdowns, signing metadata, and blacklist status.<br><br>\n\n**APY Information**: The APY data includes native lending rates, reward provider APYs (e.g., MERKLE, BREVIS), and total combined APY. All APY values are formatted to 2 decimal places and represent 7-day average market rates adjusted by fees.<br><br>\n\n**Aave pool address collision example**: On Ethereum, the following pools \nall share `poolAddress: \"0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2\"`:\n- Aave V3 USDC (underlyingToken.symbol: \"USDC\")\n- Aave V3 USDT (underlyingToken.symbol: \"USDT\") \n- Aave V3 RLUSD (underlyingToken.symbol: \"RLUSD\")\n- Aave V3 MUSD (underlyingToken.symbol: \"MUSD\")\n\nWhen rendering a token-specific pool list, filter by `underlyingToken.symbol`, \nnot by `poolAddress`.\n\nThis endpoint requires an API key and can be used to discover available investment opportunities before configuring a rebalancer or making deposits.\n\n**Rate limiting**: 1 request per 3 seconds per client IP. Excess requests receive HTTP `429` with `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers (see `RateLimitExceeded` response).\n"
operationId: getPools
tags:
- Pools
responses:
'200':
description: Pools information retrieved successfully
content:
application/json:
schema:
$ref: '#/components/schemas/GetPoolsResponse'
'401':
$ref: '#/components/responses/UnauthorizedNoHeader'
'429':
$ref: '#/components/responses/RateLimitExceeded'
'500':
$ref: '#/components/responses/InternalError'
x-agentcash-auth:
mode: free
components:
responses:
InternalError:
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
examples:
Internal Error:
value:
code: INTERNAL_ERROR
internalCode: INTERNAL_ERROR
message: An unexpected error occurred while processing your request
requestId: 2c2f3dc1-d19a-49cb-b695-37d025e551b3
retryAfterSeconds: null
UnauthorizedNoHeader:
description: No authorization header provided
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
examples:
unauthorizedNoHeader:
$ref: '#/components/examples/UnauthorizedNoHeader'
RateLimitExceeded:
description: 'Too many requests for this endpoint. Limits are enforced per client IP for the public API routes backed by `RebalancerResource` (and related resources using the same filter).
Response headers (when throttled):
- `Retry-After`: seconds to wait before retrying (matches the rate-limit window duration for that endpoint).
- `X-RateLimit-Limit`: maximum requests allowed in the window (e.g. `1`).
- `X-RateLimit-Remaining`: remaining requests in the window (`0` when throttled).
- `X-RateLimit-Reset`: Unix timestamp (seconds) when the limit window resets.
`internalCode` in the JSON body is `THROTTLE_PER_IP` for IP-scoped limits (other values may apply for different scopes in the backend).
'
headers:
Retry-After:
description: Seconds to wait before retrying this endpoint.
schema:
type: integer
example: 3
X-RateLimit-Limit:
description: Maximum number of requests allowed in the current window for this endpoint.
schema:
type: integer
example: 1
X-RateLimit-Remaining:
description: Remaining requests in the current window.
schema:
type: integer
example: 0
X-RateLimit-Reset:
description: Unix timestamp (seconds) when the current limit window resets.
schema:
type: integer
format: int64
example: 1713264123
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError'
examples:
rateLimitExceededPerIp:
summary: Rate limit exceeded (per client IP)
value:
code: RATE_LIMIT_EXCEEDED
internalCode: THROTTLE_PER_IP
message: Rate limit exceeded. Maximum 1 requests per 3 seconds allowed. Please try again later.
requestId: 2c2f3dc1-d19a-49cb-b695-37d025e551b3
retryAfterSeconds: 3
schemas:
GetPoolsResponse:
type: object
required:
- pools
properties:
pools:
type: array
items:
$ref: '#/components/schemas/Pool'
Pool:
description: "**Important: `poolAddress` is NOT globally unique across tokens.** For Aave V3, the same `poolAddress` (the Aave lending pool contract) is shared across all tokens on a given chain. For example, Aave V3 USDC, USDT, and RLUSD on Ethereum all have the same `poolAddress`.\nA pool is uniquely identified by the combination of THREE fields together:\n - `poolAddress`\n - `chain`\n - `underlyingToken.symbol`\n\nWhen filtering pools by token (e.g. \"show only USDC pools\"), ALWAYS filter on `underlyingToken.symbol`. Never filter on `poolAddress` alone for Aave pools.\n"
type: object
required:
- protocolName
- protocolVersion
- protocolDescription
- protocolUrl
- poolAddress
- poolName
- chain
- chainId
- underlyingToken
- poolTvl
- poolLiquidity
- poolApy
- signData
- blacklisted
properties:
protocolName:
type: string
description: Protocol name
example: Aave
protocolVersion:
type: string
description: Protocol version
example: V3
protocolDescription:
type: string
description: Protocol description
protocolUrl:
type: string
format: uri
description: Protocol website URL
poolAddress:
type: string
description: Pool contract address. The pool contract address. For Aave V3 pools, this is the Aave lending pool contract address, which is the SAME for all tokens (USDC, USDT, RLUSD, MUSD) on a given chain. Do NOT use this field alone as a unique pool identifier — it must always be combined with `chain` and `underlyingToken.symbol`. For Morpho and Euler, this address IS unique per pool
example: '0xabcdefabcdefabcdefabcdefabcdefabcdefabcd'
poolName:
type: string
description: Pool name
chain:
type: string
description: Chain name
example: linea
chainId:
type: integer
description: Chain ID
example: 59144
underlyingToken:
$ref: '#/components/schemas/Token'
poolTvl:
type: string
description: Total Value Locked for the pool
poolLiquidity:
type: string
description: Available liquidity for the pool
collateralExposure:
type: object
additionalProperties:
type: string
nullable: true
description: 'Collateral exposure for the pool. Keys are Morpho market identifiers, values are the collateral token symbols for each market. Null for non-Morpho pools (Aave, Euler).
'
example:
'0x3b3769cfb1649e84374e7f0e0e46cbf9a1a39a62fa6a8b82e9e9edd1b3a3e3f7': WETH
'0xa1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2': cbBTC
poolApy:
$ref: '#/components/schemas/PoolApyInfo'
signData:
$ref: '#/components/schemas/PoolSignData'
blacklisted:
type: boolean
description: Indicates whether the pool has been blacklisted (banned) due to a third party critical security issue. Blacklisted pools are deprecated and retained solely for backwards compatibility. New deposits are never routed to blacklisted pools, and all existing balances are automatically rebalanced out of such pools when possible (balance is large enough to pay on-behalf fee and there is enough liquidity to withdraw).
PoolApyInfo:
type: object
required:
- native
- rewards
- total
- totalNet
- performanceFee
properties:
native:
type: string
description: Native APY percentage (formatted to 2 decimals)
example: '3.45'
rewards:
type: object
additionalProperties:
type: string
description: Map of reward tokens to their APYs (formatted to 2 decimals)
example:
Merkle: '1.23'
Brevis: '0.87'
total:
type: string
description: Total APY percentage (formatted to 2 decimals)
example: '5.55'
totalNet:
type: string
description: Total net APY percentage (formatted to 2 decimals)
example: '4.55'
performanceFee:
type: string
description: Performance fee percentage (formatted to 2 decimals)
example: '1.00'
Token:
type: object
required:
- symbol
- name
- address
- decimals
properties:
symbol:
type: string
description: Token symbol
enum:
- USDC
- MUSD
- USDT
- RLUSD
example: USDC
name:
type: string
description: Token full name
example: USD Coin
address:
type: string
pattern: ^0x[a-fA-F0-9]{40}$
example: '0x1234567890123456789012345678901234567890'
description: Token contract address
decimals:
type: integer
description: Token decimals
example: 6
version:
type: string
nullable: true
description: Token contract version
PoolSignData:
type: object
required:
- protocolId
- poolAddress
- domainId
properties:
protocolId:
type: integer
description: Protocol ID (0=Aave, 1=Morpho, 2=Euler)
poolAddress:
type: string
description: Pool contract address
domainId:
type: integer
description: Domain ID for the chain
ApiError:
type: object
required:
- code
- internalCode
- message
properties:
code:
type: string
description: Error category identifier
internalCode:
type: string
description: More specific error identifier
message:
type: string
description: Human-readable error message
requestId:
type: string
format: uuid
description: Unique request identifier for tracking
retryAfterSeconds:
type: integer
description: Seconds to wait before retrying (for rate limiting)
examples:
UnauthorizedNoHeader:
summary: No authorization header
value:
code: AUTHENTICATION_FAILED
internalCode: NO_AUTH_INFO
message: No authentication information provided
requestId: 2c2f3dc1-d19a-49cb-b695-37d025e551b3
retryAfterSeconds: null
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-Key
description: API key required for all endpoints.
JwtAuth:
type: apiKey
in: header
name: Authorization
description: 'JWT issued by POST /v1/auth/verify. Pass as Authorization: Bearer <token>.
x-agent-hint: agentcash and other agentic wallets strip the Authorization header for their own SIWX flow. Use X-MetaLend-JWT: Bearer <token> instead — the backend accepts both headers. Affected endpoints: POST /v1/deposits, POST /v1/withdrawals, PUT /v1/config.
'
OriginHeader:
type: apiKey
in: header
name: Origin
description: 'Required for CORS requests from browsers. Must match the allowed origins configured for the API key. Example: Origin: https://app.metalend.tech. Localhost is always allowed but must be specified.
'
externalDocs:
description: Litepaper
url: https://metalend-inc.gitbook.io/litepaper