QuickNode Earn Public API (1.3.0)

Download OpenAPI specification:

HTTP API to drive Earn strategies end to end.

The Earn public API (/v1) drives the full strategy lifecycle over HTTP: browse vaults, create a strategy, fetch ready-to-sign approval and deposit/withdraw calldata, broadcast it, and poll to completion.

Lifecycle

  1. GET /v1/config — chains, minimum sizes, contract address.
  2. GET /v1/wallets/{addr}/balances — where the USDC is.
  3. GET /v1/vaults / GET /v1/vaults/rankings — pick vaults or preview an allocation.
  4. POST /v1/wallets/{addr}/tos then GET /v1/wallets/{addr}/approvals — one-time consent, then sign any missing approvals.
  5. POST /v1/strategies — create.
  6. POST /v1/strategies/{id}/calldata/deposit — broadcast the returned transaction.
  7. GET /v1/strategies/{id} (+ /performance, /history, /bridges, /intents/{intentId}) — monitor.
  8. POST /v1/strategies/{id}/calldata/withdraw or DELETE /v1/strategies/{id} — close.

Auth model

  • Reads are public, rate-limited per IP.
  • Writes are SIWE-signed. Create/update/delete and wallet-pref writes carry a per-action Sign-In-With-Ethereum proof in the request body; the recovered wallet (not any body field) is the authoritative owner. The SignedRequest component documents the exact message format.
  • Calldata endpoints are public (no SIWE). The on-chain signature is the real authorization boundary; rate limiting (per strategy + IP) is the abuse control.
  • An apikey header is required on every request. The key is public by design; see components.securitySchemes.

Units

  • Read models (/v1/vaults, /v1/strategies, detail, history, bridges) return decimal-dollar JSON numbers for USDC values: total_value_usdc: 1234.56 means $1,234.56.
  • APY fields are percentage numbers, not fractions: apy: 5.423 means 5.423% APY. Realized APY is unbounded and may be negative.
  • Transaction and approval templates (transactions[], approvalsNeeded[], balances, approvals) carry base-unit strings: USDC is a 6-decimal integer string ("1000000" = 1 USDC), native gas is wei, shares and allowances are raw uint256 decimal strings. Strings so values above 2^53 never lose precision.

Stability

v1 is additive-only: new optional fields and endpoints may appear; existing fields will not change type or disappear without a version bump. One launch-week exception (2026-07-20): vault rankings moved to GET /v1/vaults/rankings, wallet approvals became multi-chain, and live vault liquidity folded into the vault detail read.

SIWE hardening (2026-07-21, v1.2.0): signed messages carry a - path: Resources entry binding the proof to the request path, and nonces are single-use. A proof without a matching - path: entry is rejected with siwe_payload, and nonce reuse is rejected with siwe_replay. SignedRequest has the rules.

APY-gap floors (2026-07-28, v1.3.0): both threshold fields now carry hard minimums — same-network >= 3 and effective cross-network >= 5, rising to 5/7 when the strategy's networks include Ethereum. Values below a floor are rejected with a 400. An omitted delta_pct on create now defaults to the same-network floor (previously a legacy 0.1); strategies created before the floors keep running unchanged until an update touches a threshold field.

Discovery

Machine-readable API index and the self-served OpenAPI document.

Machine-readable API index

The API name, version, a link to the OpenAPI document, and a one-line list of every operation.

Authorizations:
apikey

Responses

Response samples

Content type
application/json
{
  • "name": "QuickNode Earn Public API",
  • "description": "The Earn public API (`/v1`) drives the full strategy lifecycle over HTTP ...",
  • "version": "1.1.0",
  • "endpoints": [
    ]
}

This OpenAPI 3.1 document

This document as JSON, so the full contract can be fetched from the API itself.

Authorizations:
apikey

Responses

Response samples

Content type
application/json
{ }

Platform

Deployment config, platform-wide stats, and native gas-token USD prices.

Platform-wide aggregate stats

Total USDC under management, active strategy count, and total rebalances executed. total_usdc is a best-effort live figure and may fall back to the last-known aggregate.

Authorizations:
apikey

Responses

Response samples

Content type
application/json
{
  • "total_usdc": 2148302.55,
  • "active_strategies": 214,
  • "total_rebalances": 8831,
  • "updated_at": "2026-07-13T18:00:00.000Z"
}

Native gas-token USD prices

Latest USD price for each supported native gas token (ETH, POL, MON), updated hourly. chains maps each supported chain id to the USD price of that chain's native gas token.

Authorizations:
apikey

Responses

Response samples

Content type
application/json
{
  • "ETH": 3421.07,
  • "POL": 0.412,
  • "MON": 1.85,
  • "updated_at": "2026-07-13T17:00:00.000Z",
  • "chains": {
    }
}

Public deployment config

The Earn contract address (the same on every chain) and per-chain display names and minimum strategy sizes. A chain missing from chains is not currently served. minStrategyUsdc is sizing guidance only, not enforced by POST /v1/strategies. banner is the first-party announcement strip the Earn app renders above its nav, and is null whenever none is active. Responses carry Cache-Control: public, max-age=300.

Authorizations:
apikey

Responses

Response samples

Content type
application/json
{
  • "earnContract": "0x0ea99e5e2f70e6ad0aa1a2b1a0b0d0e0f0a0b0c0",
  • "chains": [
    ],
  • "banner": {
    }
}

Vaults

Vault browse/detail/APY-history reads and the ranked allocation preview.

Vault list (browse)

Every approved vault with the seven windowed APYs, TVL, liquidity and capacity from the latest snapshot. For a capital-aware allocation preview, use GET /v1/vaults/rankings.

Authorizations:
apikey
query Parameters
chains
string
Examples: chains=8453,42161

CSV of chain ids. Defaults to all enabled chains.

window
integer [ 1 .. 360 ]
Default: 5

Minutes for the headline apy, snapped to the nearest precomputed column. The seven windowed APYs are always returned in windows.

minTvl
number >= 0
Default: 0

Minimum vault TVL in decimal dollars.

minLiquidity
number >= 0
Default: 0

Minimum withdrawable liquidity in decimal dollars.

includeCovered
boolean
Default: false

Opt in to OpenCover covered fee-wrapper rows (covered: true). Omitted or any value other than true, the list contains plain vaults only, so existing integrations never see wrapper rows unexpectedly.

Responses

Response samples

Content type
application/json
{
  • "mode": "browse",
  • "window": 5,
  • "vaults": [
    ]
}

Ranked allocation preview

The vaults a strategy with these settings would target, ranked by the same pipeline that plans deposits, enriched with liquidity/capacity from the latest snapshot. capital and maxPositions drive the ranking; the remaining params filter.

Authorizations:
apikey
query Parameters
capital
required
number > 0
Examples: capital=1000

Capital to allocate, in USDC decimal dollars (> 0).

maxPositions
required
integer >= 1
Examples: maxPositions=5

Maximum number of vault positions (positive integer).

chains
string
Examples: chains=8453,42161

CSV of chain ids. Defaults to all enabled chains.

minTvl
number >= 0
Default: 0

Minimum vault TVL in decimal dollars.

minLiquidity
number >= 0
Default: 0

Minimum withdrawable liquidity in decimal dollars.

minLiquidityMultiplier
number >= 0
Default: 2

Entry liquidity threshold multiplier: a vault must have this multiple of the intended position size in withdrawable liquidity to qualify. The deposit calldata path uses a looser default of 1.

apySmoothingMinutes
integer
Default: 30

APY smoothing window in minutes; snaps to the nearest precomputed column (5/10/30/60/120/240/360).

hiddenVaultKeys
string
Examples: hiddenVaultKeys=8453:0xabc...,42161:0xdef...

CSV of chainId:0xaddress vault keys to exclude.

wallet
string (Address) ^0x[0-9a-fA-F]{40}$
Examples: wallet=0x1234567890abcdef1234567890abcdef12345678

If present, merges this wallet's saved global hide list into the exclusions.

covered
string
Enum: "true" "false"

RANKED mode only. Coverage partition to rank within: true = OpenCover fee-wrappers only, false = plain vaults only. Omit for an unscoped ranking across both partitions. Any other value is a 400. Threaded into the ranking pipeline's strategyCovered (the covered/uncovered wizard toggle drives it).

Responses

Response samples

Content type
application/json
{
  • "mode": "ranked",
  • "vaults": [
    ],
  • "excludedChains": [
    ],
  • "eligibleCount": 2,
  • "estimatedApy": 5.7995
}

Single approved-vault detail

The latest snapshot for one approved vault: the seven windowed APYs, a headline apy at the requested window, TVL, liquidity and capacity. Same row shape as one element of the browse list. An unknown or delisted vault is a 404.

include=liveLiquidity adds a fresh on-chain read of the vault's withdrawable liquidity (liveLiquidity). This is a live number and can differ from the snapshot availableLiquidityUsd. If the on-chain read fails, the snapshot detail still returns 200 with liveLiquidity: null and liveLiquidityError: "rpc_error".

Authorizations:
apikey
path Parameters
chainId
required
integer >= 1
Examples: 8453

Chain id the vault lives on.

address
required
string (Address) ^0x[0-9a-fA-F]{40}$
Examples: 0x1234567890abcdef1234567890abcdef12345678

Vault (ERC-4626 share token) address.

query Parameters
window
integer [ 1 .. 360 ]
Default: 5

Minutes for the headline apy, snapped to the nearest precomputed column. The seven windows are returned regardless.

include
string
Value: "liveLiquidity"

CSV of extras. Supported value — liveLiquidity.

Responses

Response samples

Content type
application/json
Example
{
  • "chainId": 8453,
  • "vaultAddress": "0x616a4e1db48e22028f6bbf20444cd3b8e3273738",
  • "name": "Seamless USDC Vault",
  • "apy": 6.48,
  • "windows": {
    },
  • "tvlUsd": 12500000,
  • "availableLiquidityUsd": 3400000,
  • "maxDepositUsd": null,
  • "atCapacity": false,
  • "latestAt": "2026-07-07T18:55:00.000Z"
}

Vault APY over a caller-chosen time interval

The 5-minute snapshot series over [from, to], plus intervalApy: the annualized share-price return between the first and last snapshot in the range, (last/first)^(year/elapsed) - 1, in percent.

Retention is 7 days: an earlier from is pulled forward and clamped is set true. resolution downsamples by keeping the last snapshot per bucket (real snapshots, never averages) and never changes intervalApy.

Authorizations:
apikey
path Parameters
chainId
required
integer >= 1
Examples: 8453

Chain id the vault lives on.

address
required
string (Address) ^0x[0-9a-fA-F]{40}$
Examples: 0x1234567890abcdef1234567890abcdef12345678

Vault (ERC-4626 share token) address.

query Parameters
from
string <date-time>
Examples: from=2026-07-05T00:00:00.000Z

Start instant (ISO-8601). Default the trailing 24h before to. Clamped forward to the 7-day retention floor.

to
string <date-time>
Examples: to=2026-07-07T00:00:00.000Z

End instant (ISO-8601). Defaults to now.

resolution
integer [ 5 .. 1440 ]
Examples: resolution=60

Downsample bucket in minutes (5-1440). Omit for the full 5-minute series.

Responses

Response samples

Content type
application/json
{
  • "chainId": 8453,
  • "vaultAddress": "0x616a4e1db48e22028f6bbf20444cd3b8e3273738",
  • "from": "2026-07-06T18:00:00.000Z",
  • "to": "2026-07-07T18:00:00.000Z",
  • "clamped": false,
  • "retentionDays": 7,
  • "resolutionMinutes": 60,
  • "intervalApy": 6.4213,
  • "snapshots": [
    ]
}

Wallets

Per-wallet balances, approval templates, preferences, and terms acceptance.

USDC + native gas balance per chain

One entry per requested chain (default: all supported). Amounts are base-unit strings (USDC 6dp integer string, native in wei); nativeUsdPrice is the chain's native gas-token USD price in decimal dollars (null when unavailable). Per-chain error isolation: a failed RPC marks that chain error: "rpc_error" and never fails the whole response.

Authorizations:
apikey
path Parameters
addr
required
string (Address) ^0x[0-9a-fA-F]{40}$
Examples: 0x1234567890abcdef1234567890abcdef12345678

Wallet address (0x + 40 hex).

query Parameters
chains
string
Examples: chains=8453

CSV of supported chain ids. Defaults to all.

Responses

Response samples

Content type
application/json
{
  • "address": "0x1234567890abcdef1234567890abcdef12345678",
  • "chains": [
    ]
}

Allowances toward the proxy + ready-to-sign approve templates

Current USDC and per-vault share-token allowances toward the Earn contract (the spender), grouped per chain. Each entry carries an approved flag and, when not approved, a ready-to-sign approve(spender, amount) template. A share token is approved when allowance > 0; USDC is approved when allowance >= requiredUsdc. Allowances and amounts are base-unit strings. Per-chain error isolation: a failed RPC marks that chain error: "rpc_error" and never fails the whole response.

Authorizations:
apikey
path Parameters
addr
required
string (Address) ^0x[0-9a-fA-F]{40}$
Examples: 0x1234567890abcdef1234567890abcdef12345678

Wallet address (0x + 40 hex).

query Parameters
chains
string
Examples: chains=8453,42161

CSV of supported chain ids. Defaults to all.

requiredUsdc
string^[0-9]+$
Default: "0"
Examples: requiredUsdc=1000000000

USDC the pending deposit needs, in 6-decimal base units, evaluated against each requested chain's USDC allowance. Default 0 (report-only).

vaults
string
Examples: vaults=8453:0x616a...,42161:0x1a99...

CSV of chainId:0xaddress vault keys to report. Each key applies only to its own chain; a requested chain with no listed vaults still returns its USDC entry. A key targeting a chain outside the requested set is a 400. Defaults to every approved vault per chain.

Responses

Response samples

Content type
application/json
{
  • "address": "0x1234567890abcdef1234567890abcdef12345678",
  • "chains": [
    ]
}

Read a wallet's preferences (public)

ToS state, global hide list, the approval-banner flag, and the covered-access signals. Unknown wallets return defaults (empty hide list, both covered flags false). approved is a legacy field and always true. covered_access / opencover_terms_accepted are SOFT wizard signals only; the hard covered boundary is enforced at strategy create.

Authorizations:
apikey
path Parameters
addr
required
string (Address) ^0x[0-9a-fA-F]{40}$
Examples: 0x1234567890abcdef1234567890abcdef12345678

Wallet address (0x + 40 hex).

Responses

Response samples

Content type
application/json
{
  • "address": "0x1234567890abcdef1234567890abcdef12345678",
  • "has_agreement": true,
  • "hidden_vault_keys": [
    ],
  • "hide_approval": false,
  • "covered_access": false,
  • "opencover_terms_accepted": false
}

Update a wallet's preferences (SIWE `prefs.update`)

Updates the global hide list and/or the approval-banner flag in one signed payload. SignedRequest, action prefs.update. The recovered wallet must equal the path addr or the call fails 403 owner_mismatch. At least one of hide_vault_key, unhide_vault_key, hide_approval is required. Hides and unhides merge into the stored list; the full resulting list is returned.

Hiding a held vault is allowed (it can force-exit positions). The response then carries an advisory warnings array listing each active strategy whose eligible pool drops below its max_positions + 1 floor.

Authorizations:
apikey
path Parameters
addr
required
string (Address) ^0x[0-9a-fA-F]{40}$
Examples: 0x1234567890abcdef1234567890abcdef12345678

Wallet address (0x + 40 hex).

Request Body schema: application/json
required
required
object (SiweBlock)

A Sign-In-With-Ethereum proof binding the wallet to (action, payload, issuedAt, expirationTime).

hide_vault_key
string^\d+:0x[0-9a-fA-F]{40}$

Vault key to add to the hide list.

unhide_vault_key
string^\d+:0x[0-9a-fA-F]{40}$

Vault key to remove from the hide list.

hide_approval
boolean

Set the approval-banner dismissal flag.

Responses

Request samples

Content type
application/json
{
  • "hide_vault_key": "8453:0x616a4e1db48e22028f6bbf20444cd3b8e3273738",
  • "hide_approval": true,
  • "siwe": {
    }
}

Response samples

Content type
application/json
{
  • "ok": true,
  • "hidden_vault_keys": [
    ],
  • "hide_approval": true
}

Record a wallet's ToS agreement (SIWE `tos_agreement`)

Records the wallet's ToS acceptance. SignedRequest, action tos_agreement; the signature binds terms_url, which is echoed back. The recovered wallet must equal the path addr or the call fails 403 owner_mismatch.

Authorizations:
apikey
path Parameters
addr
required
string (Address) ^0x[0-9a-fA-F]{40}$
Examples: 0x1234567890abcdef1234567890abcdef12345678

Wallet address (0x + 40 hex).

Request Body schema: application/json
required
required
object (SiweBlock)

A Sign-In-With-Ethereum proof binding the wallet to (action, payload, issuedAt, expirationTime).

terms_url
string <uri>

The ToS URL being agreed to; echoed back in the response.

Responses

Request samples

Content type
application/json
{
  • "siwe": {
    }
}

Response samples

Content type
application/json
{}

Record a wallet's OpenCover terms acknowledgment (SIWE `opencover_terms`)

Stores the wallet's acknowledgment that it viewed OpenCover's terms before selecting COVERED in the strategy wizard. SignedRequest; action opencover_terms (statement Authorize opencover_terms). Unlike the platform ToS, the FULL signed SIWE message and the raw signature are PERSISTED as proof (a third party's terms), keyed one-row-per-wallet with last-write-wins semantics. The addr is in the PATH; the recovered wallet MUST equal it or the call is a 403 owner_mismatch. The signed terms_url binds which terms version was acknowledged and is echoed back. This ack is ENFORCED at covered strategy create (403 covered_terms_required), not merely client-gated.

Authorizations:
apikey
path Parameters
addr
required
string (Address) ^0x[0-9a-fA-F]{40}$
Examples: 0x1234567890abcdef1234567890abcdef12345678

Wallet address (0x + 40 hex).

Request Body schema: application/json
required
required
object (SiweBlock)

A Sign-In-With-Ethereum proof binding the wallet to (action, payload, issuedAt, expirationTime).

terms_url
string <uri>

The OpenCover terms URL being acknowledged; bound into the signature and echoed back.

Responses

Request samples

Content type
application/json
{
  • "siwe": {
    }
}

Response samples

Content type
application/json
{
  • "ok": true,
  • "address": "0x1234567890abcdef1234567890abcdef12345678",
  • "opencover_terms_accepted": true,
}

Strategies

Strategy list/detail/performance reads and SIWE-signed lifecycle mutations.

List a wallet's strategies with positions summary

Active and closed strategies for a wallet, each with a live valuation, realized APY, fee/rebalance rollups, and a positions[] array. USDC values are decimal dollars.

Authorizations:
apikey
query Parameters
wallet
required
string (Address) ^0x[0-9a-fA-F]{40}$
Examples: wallet=0x1234567890abcdef1234567890abcdef12345678

Owner wallet address.

Responses

Response samples

Content type
application/json
{
  • "strategies": [
    ],
  • "closedStrategies": [
    ]
}

Create a strategy (SIWE `strategy.create`)

Creates a strategy in pending_setup and returns it wrapped in a strategy object (201). Body is a SignedRequest, action strategy.create; the signed payload is the body minus siwe. The recovered wallet owns the strategy; any wallet field in the body is ignored.

Optional idempotencyKey (string, 1-128 chars, else 400) rides inside the signed payload and is scoped per wallet. A repeated key returns the original stored response verbatim, so do not reuse a key across different requests.

Authorizations:
apikey
Request Body schema: application/json
required
required
object (SiweBlock)

A Sign-In-With-Ethereum proof binding the wallet to (action, payload, issuedAt, expirationTime).

name
required
string <= 50 characters
capital_usdc
required
number

Capital in decimal dollars.

delta_pct
number

Same-network APY-gap threshold (percentage points). Optional: when omitted it defaults to the same-network floor. Minimum 3, or 5 when the strategy's networks include Ethereum (chain id 1) — a present value below the floor is rejected with a 400.

cross_chain_delta_pct
number

APY-gap threshold (percentage points) required when the source and destination vaults are on different chains. When omitted, defaults to 0 (inherit delta_pct), so integrations that predate this field keep single-threshold behaviour: one bar gates every move, and later delta_pct updates move both. A split bar is always an explicit opt-in. For a multi-network strategy the EFFECTIVE cross bar (this value when > 0, else the inherited delta_pct) must be >= 5, or >= 7 when the networks include Ethereum — requests below the floor are rejected with a 400.

delta_confirmations
integer
max_positions
required
integer >= 1
min_tvl_usd
number
min_liquidity_usd
number
min_liquidity_entry_multiplier
number

Multiplier on min_liquidity_usd for the non-held entry threshold. Defaults to 2 when omitted.

chain_id
integer

Optional primary chain id; defaults to the first supported chain.

chain_ids
Array of integers

Chain ids to span. Omit for a single-chain strategy on chain_id.

apy_smoothing_minutes
integer

Defaults to 30 when omitted.

covered
boolean

Opt the strategy into OpenCover coverage. Set once at create and immutable thereafter. When true the strategy is forced Base-only (chain_ids → "8453") and the wallet MUST be on the covered-access allowlist AND have acknowledged OpenCover's terms, else the create is a 403 (covered_not_allowed / covered_terms_required). Defaults to false (uncovered).

hidden_vault_keys
Array of strings[ items^\d+:0x[0-9a-fA-F]{40}$ ]
idempotencyKey
string [ 1 .. 128 ] characters

Optional dedupe key (part of the signed payload). A repeat returns the ORIGINAL stored response verbatim, so never reuse a key across logically different requests. Malformed values are 400 invalid_request.

Responses

Request samples

Content type
application/json
{
  • "name": "My USDC auto-pilot",
  • "capital_usdc": 1000,
  • "delta_pct": 3,
  • "cross_chain_delta_pct": 5,
  • "delta_confirmations": 2,
  • "max_positions": 2,
  • "min_tvl_usd": 1000000,
  • "min_liquidity_usd": 50000,
  • "chain_ids": [
    ],
  • "apy_smoothing_minutes": 30,
  • "hidden_vault_keys": [ ],
  • "idempotencyKey": "b3d1c2e4-0000-4000-8000-000000000001",
  • "siwe": {
    }
}

Response samples

Content type
application/json
{
  • "strategy": {
    }
}

Strategy detail

Single-strategy detail: config, live-valued positions, cycle/gauge fields (cycle_state, swap_signals, last_skip, …), closed positions, and the same computed rollups as the list. Poll this endpoint to watch a status transition (e.g. pending_setupactive, closingclosed).

Authorizations:
apikey
path Parameters
id
required
string <uuid>

Strategy UUID.

Responses

Response samples

Content type
application/json
{
  • "strategy": {
    }
}

Update strategy config (SIWE `strategy.update`)

Updates config fields (delta, min TVL/liquidity, hide list, name, and status for pause/resume — only active and paused are accepted). SignedRequest, action strategy.update; the id rides in the path and the proof's - path: entry binds it. Only fields present in the body are written; explicit null writes null.

Hide-list toggles: hide_vault_key / unhide_vault_key merge a single key server-side, so a toggle cannot clobber concurrent edits. Exactly one op per request; combining an op with a hidden_vault_keys array replace is a 400 invalid_request.

A hide op or array replace that would leave fewer than max_positions + 1 eligible vaults is a 409 hide_floor_violated. Hiding a never-eligible vault passes; unhide_vault_key is not checked.

Authorizations:
apikey
path Parameters
id
required
string <uuid>

Strategy UUID.

Request Body schema: application/json
required
required
object (SiweBlock)

A Sign-In-With-Ethereum proof binding the wallet to (action, payload, issuedAt, expirationTime).

name
string <= 50 characters
capital_usdc
number

Decimal dollars. Written verbatim when present; no balance check on update (unlike create).

delta_pct
number

Same-network APY-gap threshold (percentage points). When either threshold field is present, the effective post-update pair is validated against the floors: delta_pct >= 3 (5 when the strategy's networks include Ethereum). Strategies created below the floors keep running unchanged until a PATCH touches a threshold field.

cross_chain_delta_pct
number

Cross-chain APY-gap threshold. 0 means inherit delta_pct — send 0 (not null; the column is non-nullable) to reset an explicit bar back to inherit. For a multi-network strategy the EFFECTIVE cross bar (this value when > 0, else delta_pct) must be >= 5, or >= 7 when the networks include Ethereum — validated whenever either threshold field is in the payload.

delta_confirmations
integer
max_positions
integer
min_tvl_usd
number
min_liquidity_usd
number
min_liquidity_entry_multiplier
number
apy_smoothing_minutes
integer
hidden_vault_keys
Array of strings[ items^\d+:0x[0-9a-fA-F]{40}$ ]

Full-array REPLACE of the strategy hide list. Cannot be combined with the single-op toggles below.

hide_vault_key
string^\d+:0x[0-9a-fA-F]{40}$

Single-op: add this vault key to the strategy's hide list (lowercased, deduped server-side).

unhide_vault_key
string^\d+:0x[0-9a-fA-F]{40}$

Single-op: remove this vault key from the strategy's hide list.

status
string
Enum: "active" "paused"

Responses

Request samples

Content type
application/json
{
  • "siwe": {
    },
  • "name": "string",
  • "capital_usdc": 0,
  • "delta_pct": 0,
  • "cross_chain_delta_pct": 0,
  • "delta_confirmations": 0,
  • "max_positions": 0,
  • "min_tvl_usd": 0,
  • "min_liquidity_usd": 0,
  • "min_liquidity_entry_multiplier": 0,
  • "apy_smoothing_minutes": 0,
  • "hidden_vault_keys": [
    ],
  • "hide_vault_key": "string",
  • "unhide_vault_key": "string",
  • "status": "active"
}

Response samples

Content type
application/json
{
  • "strategy": {
    }
}

Delete or close a strategy (SIWE `strategy.delete`)

Deletes a pre-deposit strategy; closes a funded strategy that needs no on-chain withdrawal (the fallback: "delete" target of POST .../calldata/withdraw). SignedRequest, action strategy.delete, empty signed payload ({}); the id rides in the path and the proof's - path: entry binds it.

Authorizations:
apikey
path Parameters
id
required
string <uuid>

Strategy UUID.

Request Body schema: application/json
optional
required
object (SiweBlock)

A Sign-In-With-Ethereum proof binding the wallet to (action, payload, issuedAt, expirationTime).

txHash
string

Close tx hash (legacy envelope).

finalValueUsdc
number

On-chain final value in decimal dollars (legacy envelope), covering the whole close. Withdrawal amounts already recorded from on-chain close events always take precedence: only the excess of this value above the recorded event total is counted (floored at zero) and stands in for the legs those events do not cover, so the recorded events win over a stale or lower total.

object

Per-chain close data keyed by chain id (string).

Responses

Request samples

Content type
application/json
{
  • "siwe": {
    },
  • "txHash": "string",
  • "finalValueUsdc": 0,
  • "perChain": {
    }
}

Response samples

Content type
application/json
Example
{
  • "success": true,
  • "action": "deleted"
}

Strategy event log

Up to 100 events (deposits, rebalances, withdrawals, cross-chain bridge-backs), newest-first. forced marks a liquidity- or hide-driven exit from a higher-APY vault.

?format=entries merges the same events with the strategy's CCTP transfers (limit 50) into one entry per action, each with per-chain phases.

Authorizations:
apikey
path Parameters
id
required
string <uuid>

Strategy UUID.

query Parameters
format
string
Default: "events"
Enum: "events" "entries"

Response shape. events (default) is the flat event log (HistoryResponse); entries is the aggregated accordion view (HistoryEntriesResponse).

Responses

Response samples

Content type
application/json
Example
{
  • "events": [
    ]
}

CCTP bridge transfers for a strategy

The strategy's cross-chain (CCTP) transfers, newest-first, limit 50. amount_usdc is a string; fee_usdc is a number or null.

Authorizations:
apikey
path Parameters
id
required
string <uuid>

Strategy UUID.

Responses

Response samples

Content type
application/json
{
  • "transfers": [
    ]
}

Strategy value/yield time series

Yield snapshots (~55-min cadence) for the trailing hours, oldest-first, plus period_fee_usdc and net_yield (see YieldSnapshot). USDC values are decimal dollars; weighted_apy is a percentage. Unknown strategy: 404. Capped at 2000 rows keeping the newest; truncated: true flags a capped response.

Authorizations:
apikey
path Parameters
id
required
string <uuid>

Strategy UUID.

query Parameters
hours
integer [ 1 .. 2160 ]
Default: 168

Trailing window in hours. Defaults to 168 (7d); clamped to [1, 2160] (90d).

Responses

Response samples

Content type
application/json
{
  • "hours": 168,
  • "truncated": false,
  • "snapshots": [
    ]
}

Single intent lifecycle status

Poll the lifecycle of an intent returned by the calldata endpoints.

status is derived:

  • failed: a failure reason was recorded (including pre-transaction failures); fulfilled_tx_hash is always null.
  • fulfilled: fulfilled_at is set and the intent did not fail.
  • expired: unfulfilled and past expires_at.
  • pending: everything else.

404 intent_not_found covers both a missing intent and an intent belonging to another strategy. Raw calldata is never returned.

Authorizations:
apikey
path Parameters
id
required
string <uuid>

Strategy UUID.

intentId
required
string <uuid>

Intent id, returned as intentId by the calldata endpoints.

Responses

Response samples

Content type
application/json
{
  • "id": "7f3a1c2b-0000-4000-8000-000000000abc",
  • "strategy_id": "b7c1e2d3-0000-4000-8000-000000000001",
  • "chain_id": 8453,
  • "kind": "deposit",
  • "created_at": "2026-07-07T18:00:00.000Z",
  • "expires_at": "2026-08-06T18:00:00.000Z",
  • "status": "fulfilled",
  • "fulfilled_at": "2026-07-07T18:04:12.000Z",
  • "fulfilled_tx_hash": "0x9c1f…",
  • "submitted_tx_hash": null,
  • "failure_reason": null
}

Calldata

Ready-to-sign deposit/withdraw/emergency-claim transaction generation.

Generate deposit calldata (public, no SIWE)

Returns one ready-to-sign selfBatchDeposit transaction on the strategy's chain, covering local vault deposits plus per-remote-chain CCTP burns. The matching intent (kind deposit) is written server-side; do not post an intent separately. No auth; no request body (the id is in the path).

Strategy must be pending_setup (top-ups are not supported).

Timing: expiresAt is an advisory ~1h plan-freshness bound. The intent itself has a 30-day TTL; regenerating an unchanged plan within it returns the same intentId, and regenerating after expiry or fulfillment mints a new one. A strategy that has never received a deposit is purged about one hour after it was last touched, taking its intent with it; calling this endpoint refreshes that hour. If signing takes longer than an hour (e.g. a multisig), regenerate at least hourly until the transaction is broadcast.

Approve vault share tokens and USDC before calling: live allowances affect vault selection, and approvalsNeeded reflects what is still missing.

Submit the returned data verbatim (re-encoding breaks intent attribution) and use gasHint as the gas limit (wallet re-estimates can run out of gas).

Authorizations:
apikey
path Parameters
id
required
string <uuid>

Strategy UUID.

Request Body schema: application/json
optional
object (CalldataRequest)

Deposit calldata request body. Public (no SIWE). The deposit endpoint takes no parameters, so send no body or an empty JSON object ({}).

Responses

Request samples

Content type
application/json
{ }

Response samples

Content type
application/json
{
  • "kind": "deposit",
  • "transactions": [
    ],
  • "approvalsNeeded": [ ],
  • "plan": {
    },
  • "expiresAt": "2026-07-07T19:00:00.000Z"
}

Generate withdraw/close calldata (public, no SIWE)

Returns one ready-to-sign selfBatchWithdraw transaction per chain, source chain first then ascending chain id. Sign and submit in that order, data verbatim. Paused and zero-share positions are excluded. Each transaction has its own intentId (kind close), written server-side. No auth; no status gate. Optional chainIds in the JSON body restricts the plan to those chains.

If nothing is withdrawable, transactions is [] and fallback is "delete": close via DELETE /v1/strategies/{id} instead.

expiresAt and intent TTL semantics match the deposit endpoint (the pre-deposit purge does not apply).

Authorizations:
apikey
path Parameters
id
required
string <uuid>

Strategy UUID.

Request Body schema: application/json
optional
chainIds
Array of integers

Optional subset of chain ids to withdraw from. Omit to close all chains.

Responses

Request samples

Content type
application/json
Example
{ }

Response samples

Content type
application/json
Example
{
  • "kind": "withdraw",
  • "transactions": [
    ],
  • "approvalsNeeded": [ ],
  • "plan": {
    },
  • "expiresAt": "2026-07-07T19:00:00.000Z"
}

Generate emergency CCTP claim calldata (public, no SIWE)

Escape hatch for a stalled deposit bridge leg: fetches the finalized Circle attestation and returns one ready-to-sign emergencyClaimBridge transaction on the destination chain. Minted USDC goes directly to the burn-time beneficiary's wallet, bypassing the destination vault deposit. The matching intent (kind emergency_claim) is written server-side. No auth.

Eligibility (each failure is a 400 invalid_state): the transfer must be a deposit leg, not already confirmed or user_claimed, at least 30 minutes old, and its recorded beneficiary must be the strategy owner. If Circle has not finalized the attestation, the response is a 409 attestation_pending; poll on that status code.

approvalsNeeded is always []. expiresAt is nominal: the claim stays valid until the message nonce is consumed on the destination chain. A null gasHint with estimation_failed can mean the nonce was already consumed (the transfer completed normally); the on-chain result is authoritative.

Authorizations:
apikey
path Parameters
id
required
string <uuid>

Strategy UUID.

Request Body schema: application/json
required
transferId
required
string <uuid>

The stalled transfer's cctp_transfers row id (find it via GET /v1/strategies/{id}/bridges).

Responses

Request samples

Content type
application/json
{
  • "transferId": "9a8b7c6d-0000-4000-8000-000000000123"
}

Response samples

Content type
application/json
{
  • "kind": "claim",
  • "transactions": [
    ],
  • "approvalsNeeded": [ ],
  • "expiresAt": "2026-07-07T19:00:00.000Z"
}