Skip to main content

Generate withdraw calldata – Earn API

Updated on
Jul 31, 2026

Overview

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

Endpoint

POST https://earn-api.quicknode.dev/functions/v1/api/v1/strategies/{id}/calldata/withdraw

Authorization

Include the published apikey header. This endpoint is public and does not require a SIWE signature.

Rate limits

This operation belongs to the calldata rate-limit bucket. It allows 10 requests per 60 seconds, keyed by strategy+ip. A coarser limit of approximately 30 requests per 60 seconds also applies per IP across deposit, withdrawal, and claim calldata requests.

Parameters

ParameterLocationTypeRequiredDescription
idpathstringYesStrategy UUID.

Request body

Withdraw/close calldata request body. Public (no SIWE).

FieldTypeRequiredDescription
chainIdsarray<integer>NoOptional subset of chain ids to withdraw from. Omit to close all chains.

Example request

curl --request POST \
--url 'https://earn-api.quicknode.dev/functions/v1/api/v1/strategies/b7c1e2d3-0000-4000-8000-000000000001/calldata/withdraw' \
--header 'Accept: application/json' \
--header 'apikey: <EARN_PUBLIC_API_KEY>' \
--header 'Content-Type: application/json' \
--data '{}'

Response

FieldTypeRequiredDescription
kindstring (withdraw)YesIdentifies the response as a withdraw calldata plan.
transactionsarray<object>YesOne per chain, source-chain-first. Empty when there is nothing to withdraw.
approvalsNeededarray<object>NoPresent on a non-empty plan; omitted on the empty (delete-fallback) plan.
expiresAtstringYes~1h ADVISORY plan-freshness bound. NOT the intent attribution window (the intent carries a 30-day TTL). Regenerate after this passes; regeneration within the intent TTL returns the same `intentId`.
planobjectYesPer-chain vault, share, fee, and bridge-burn details for the withdrawal.
fallbackstring (delete)NoPresent with value `delete` when `transactions` is empty — call `DELETE /v1/strategies/{id}` instead.
transactions[].chainIdintegerYesChain on which to submit the transaction.
transactions[].tostringYesQuicknode Earn proxy address on `chainId`.
transactions[].datastringYesTransaction calldata. Submit it verbatim.
transactions[].valuestringYesNative-token value. Always `"0"`.
transactions[].intentIdstringYesIntent UUID used to track this chain close.
transactions[].kindstring (close)YesTransaction intent kind.
transactions[].descriptionstringYesHuman-readable transaction summary.
transactions[].gasHintstring | nullYesRecommended gas limit, or `null` when it cannot be estimated.
transactions[].gasHintReasonstringNo`approvals_required` or `estimation_failed` when `gasHint` is `null`.
approvalsNeeded[].chainIdintegerYes when presentChain where approval is required.
approvalsNeeded[].tokenstringYes when presentVault share-token contract requiring approval.
approvalsNeeded[].spenderstringYes when presentQuicknode Earn proxy address.
approvalsNeeded[].currentAllowancestringYes when presentCurrent allowance in token base units.
approvalsNeeded[].requiredAllowancestringYes when presentRequired allowance in token base units.
approvalsNeeded[].txobjectYes when presentReady-to-sign token approval transaction.
approvalsNeeded[].tx.tostringYes when presentToken contract receiving the approval call.
approvalsNeeded[].tx.datastringYes when presentABI-encoded approval calldata.
approvalsNeeded[].tx.valuestringYes when presentNative-token value, normally `"0"`.
plan.chainsobjectYesWithdrawal plan keyed by chain ID.
plan.chains.<chainId>.vaultsarray<string>YesVault addresses to withdraw from.
plan.chains.<chainId>.sharesarray<string>YesRaw share amounts aligned with the vault list.
plan.chains.<chainId>.feeAmountsarray<string>YesFee amounts. User withdrawals return `"0"` entries.
plan.chains.<chainId>.totalFeeUsdcnumberYesTotal fee in decimal US dollars.
plan.chains.<chainId>.burnsarray<object>YesCCTP burn legs needed to return remote-chain USDC.

Example response

The data values below are shortened for readability. Submit the complete value returned by the API verbatim.

{
"kind": "withdraw",
"transactions": [
{
"chainId": 8453,
"to": "0x48b415841165304f7EfaA7D5dD5FC65cc7B4bd8e",
"data": "0x458c2be6…",
"value": "0",
"intentId": "7f3a1c2b-0000-4000-8000-000000000abc",
"kind": "close",
"description": "Close 1 position(s) on chain 8453",
"gasHint": "310000"
},
{
"chainId": 42161,
"to": "0x48b415841165304f7EfaA7D5dD5FC65cc7B4bd8e",
"data": "0x458c2be6…",
"value": "0",
"intentId": "8e4b2d3c-0000-4000-8000-000000000abd",
"kind": "close",
"description": "Close 1 position(s) on chain 42161, bridging USDC to the source chain",
"gasHint": "410000"
}
],
"approvalsNeeded": [],
"plan": {
"chains": {
"8453": {
"vaults": ["0x616a4e1db48e22028f6bbf20444cd3b8e3273738"],
"shares": ["599430000000000000000"],
"feeAmounts": ["0"],
"totalFeeUsdc": 0,
"burns": [
{
"destDomain": 0,
"mintRecipient": "0x0000000000000000000000001234567890abcdef1234567890abcdef12345678",
"destinationCaller": "0x0000000000000000000000000000000000000000000000000000000000000000",
"amount": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"maxFee": "0",
"minFinalityThreshold": 2000
}
]
},
"42161": {
"vaults": ["0x1a996cb54bb95462040408c06122d45d6cdb6096"],
"shares": ["399120000000000000000"],
"feeAmounts": ["0"],
"totalFeeUsdc": 0,
"burns": [
{
"destDomain": 0,
"mintRecipient": "0x0000000000000000000000001234567890abcdef1234567890abcdef12345678",
"destinationCaller": "0x0000000000000000000000000000000000000000000000000000000000000000",
"amount": "115792089237316195423570985008687907853269984665640564039457584007913129639935",
"maxFee": "0",
"minFinalityThreshold": 2000
}
]
}
}
},
"expiresAt": "2026-07-07T19:00:00.000Z"
}

HTTP responses

StatusDescription
200Per-chain withdraw transactions (source-first), approvals needed, plan transparency, and an advisory expiry. May be an empty plan with a `fallback` hint.
400A malformed JSON body or a bad `chainIds` value (`invalid_request`), or a `chainIds` subset that matches no withdrawable position (`no_positions_for_chains`).
404Resource not found.
429Rate limit exceeded. `Retry-After` (seconds) tells you when to retry. Reads, feedback, and push writes are keyed per IP; create/update/delete are keyed per wallet+IP; deposit/withdraw/claim calldata is keyed per strategy+IP.
500Internal server error.
OpenAPI source

This page is based on operation withdrawCalldata in the Earn OpenAPI 3.1 specification.

Share this doc