Withdraw funds
Choose a live withdrawal route, withdraw instantly or by request, and confirm the payout reached the customer's wallet.
A withdrawal spends shares from a position and pays the deposit token back to the wallet that owns it. Some routes pay in the same transaction; others pay later. Read the live routes first, then follow the one your customer chooses.
Set SDP_API_URL and SDP_API_KEY as shown in the
Quickstart.
1. Choose a route
POST /v1/earn/external-wallet/withdrawal-options
reads each route from live provider state.
| Route | When it is offered | Pays out |
|---|---|---|
| Instant withdrawal | instant: true | In the same transaction. |
| Provider order | providerOrder: true | Later, when the provider completes the order. |
| Solver queue | queued: true, with live terms in queueAsset | Later, at a discount, when a solver fulfills the request. |
| Operator redemption | parRedemption is present | Later, at par, when the operator settles the request. |
A position offers an instant withdrawal or a provider order, never both, and
the same withdrawal endpoints build whichever it offers. When more than one
route is offered, let your customer choose, and keep their choice. Never infer
a route from the strategy's liquidityTerm. A failed read or missing terms
means the route is unavailable, not that it pays zero.
2. Withdraw instantly
An instant withdrawal works like a deposit: preview, build, sign, submit, and
track. Withdraw at most the position's live withdrawableShares: a preview
does not check the wallet's balance.
| Step | Request |
|---|---|
| Preview | POST /v1/earn/external-wallet/withdrawal-previews |
| Build | POST /v1/earn/external-wallet/withdrawal-transactions |
| Submit | POST /v1/earn/external-wallet/withdrawals, with an Idempotency-Key |
| Track | GET /v1/earn/external-wallet/movements/{movementId} |
The example uses the Quickstart's earn-api.ts and test-wallet.ts, and
floor.ts from Slippage floors.
Call withdraw with the positionId and withdrawalSlippage saved in your
backend after the deposit, plus the share amount your customer chose:
The preview quotes the payout; it does not reserve it. The build requires
minAmountOut when the strategy's withdrawalSlippage is not null, and
refuses it when the policy is null. The position supplies the vault and mints,
so a delisted strategy stays exitable. Keep the deposit-time policy with the
position; do not require the strategy to appear in today's catalog.
If the build reports that the floor policy changed, refresh the policy with your SDP contact before retrying. Never guess a policy from a missing catalog row.
At finalized, tokenAmount is the payout SDP observed. Check the customer's
token balance onchain if you need independent proof, then refresh
balances.
Provider orders
A provider order takes an exact share amount and pays out after the provider
prices and settles it. Its preview may return 501, which does not block the
build. No floor can bound a later price, so omit minAmountOut when the
policy is null.
The withdrawal's settlement is provider_order. Confirmation proves the
order transaction, not the payout: the movement stays confirmed until the
provider completes the order. Show it as pending until it reaches finalized;
see How a movement settles.
Asynchronous withdrawals
A solver queue or an operator redemption creates a withdrawal request. The request transaction moves shares, never the payout, so track the request until it pays out or is canceled.
Solver queues
The request escrows shares. After secondsToMaturity, a solver may fulfill it
at the agreed discount until the deadline. Before the customer confirms, show
the live terms from queueAsset: minimum shares, the discount range, maturity,
and the deadline range.
Send shares, discountBps, and deadlineSeconds, each inside the live
terms; mechanism defaults to solverQueue. The deadline counts from
maturity, not from creation, and both run on the cluster's clock:
maturityTimestamp and deadlineTimestamp are onchain Unix times.
- The discount is your customer's cost. A larger
discountBpsmakes a solver more likely to fill the request, and the customer receives that much less. - The deadline is how long solvers have. A longer
deadlineSecondsgives them more time, but keeps the shares escrowed longer if none fills it. - Open requests leave the balance. Escrowed shares drop out of the
position's
sharesand earnings'currentValue, and earnings reportwithdrawals_pending. Show the request'squotedAssetsas an expected payout.
Operator redemptions
The request converts shares into an intermediate token and authorizes the
operator to settle it for the deposit token at par. Read the live
parRedemption terms: minimum shares, intermediateMint, assetMint, and
whether cancellation is allowed.
Send mechanism: "operatorRedemption" with either shares or
intermediateAmount from an existing parIntermediate holding, never both.
A null minimumShares means the conversion rate is unavailable: the preview
reports a blocking issue.
Create a request
| Step | Request |
|---|---|
| Preview | POST /v1/earn/external-wallet/queued-withdrawal-previews |
| Build | POST /v1/earn/external-wallet/withdrawal-request-transactions |
| Submit | POST /v1/earn/external-wallet/withdrawal-requests, with an Idempotency-Key |
Resolve every blockingIssues entry in the preview, then build, sign, and
submit as for an instant withdrawal. Save the returned withdrawalRequestId.
Keep the signed request and its idempotency key until the request resolves. The wallet may already have broadcast it, so SDP accepts it even after its build expires:
| Submit answer | What to do |
|---|---|
409 CONFLICT on a late request | SDP released the build's reservation and accepts the request once the exact transaction is finalized. Retry the same bytes and key. For an operator redemption whose cycle already closed, contact support instead. |
400 TRANSACTION_FAILED | The transaction finalized as failed. Build a new request. |
503 | SDP could not finish checking the request's history. Retry the same bytes and key, and contact support if it persists. |
Track a request
Poll GET /v1/earn/external-wallet/withdrawal-requests/{withdrawalRequestId}
every minute or two. Status values spell it cancelled.
| Status | Meaning | What to show |
|---|---|---|
creating | SDP recorded the signed request; its chain outcome is unresolved. | Pending. |
pending | Waiting for maturity or for the operator. | Pending, with the expected timing. |
fulfillable | A solver may fulfill it now. | Pending. |
expiredCancelable | The solver deadline passed without fulfillment. | Offer cancellation. |
cancelling | A cancellation was submitted; a fulfillment can still race it. | Canceling. |
closedOrUnknown | The request closed, and SDP has not yet proved a payout or a cancellation. | Pending; keep tracking. |
fulfilled | SDP observed the payout. assetsPaid holds the amount. | Done. |
cancelled | SDP observed the cancellation. | Canceled; refresh holdings. |
failed | It did not happen: SDP decides that only after repeated checks find no trace of it. failureReason says why. | Failed. |
Only fulfilled, cancelled, and failed are final, and only fulfilled
pays out. Never credit quotedAssets, a request transaction, or a
cancellation as received funds. A solver queue request that no solver takes
stays fulfillable until its deadline, then becomes expiredCancelable, and
its shares stay escrowed until the customer cancels it.
On startup and after reconnecting, restore every open request:
GET /v1/earn/external-wallet/withdrawal-requests?ownerAddress=<wallet>&settled=false,
reading every page.
Sign withdrawal requests and cancellations without broadcasting them, and submit them through SDP promptly. If a wallet broadcasts one itself and SDP first sees it after the build expires, SDP may not be able to track it.
Cancel a request
Offer cancellation for a solver queue request only at expiredCancelable,
never from a client clock; building one earlier returns 409 CONFLICT. An
operator redemption can be canceled while pending if its live terms allow
it. Refresh the request first: a fulfillment can race the cancellation.
| Step | Request |
|---|---|
| Build | POST /v1/earn/external-wallet/withdrawal-request-cancel-transactions |
| Submit | POST /v1/earn/external-wallet/withdrawal-request-cancellations |
Both steps take earn.manage, and cancellation stays available after
production access is removed or new deposits are disabled. Use a new
Idempotency-Key for the cancellation. A canceled solver request returns
the escrowed shares; a canceled operator redemption leaves the intermediate
token, not the original shares. Refresh holdings before another withdrawal.