Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.groundtech.co/llms.txt

Use this file to discover all available pages before exploring further.

Portfolio wallet withdrawals are asynchronous. A withdrawal may involve unwinding yield positions, bridging across chains, and waiting for customer approval — all orchestrated automatically.
Treat withdrawal creation as acceptance and reservation of a request, not as a universal price lock across every yield source. Morpho ERC-4626 vaults exposed by the live catalog are synchronous exits without a normal queue window. syrup-usdc is an asynchronous exit: if the underlying protocol settles at a lower value before processing, the final payout can be lower than the amount originally requested. syrup-usdc can also be partially processed while the remainder stays pending.

Withdrawal lifecycle

The typical flow is: preview -> initiate -> track -> complete.
  1. Preview — call withdrawal-preview to see what’s available and get a sourcing plan.
  2. Initiate — call withdrawals to create the withdrawal. The system reserves liquidity and begins sourcing.
  3. Track — poll the withdrawal or subscribe to webhooks. The withdrawal moves through statuses as positions unwind, funds bridge, and payouts execute.
  4. Complete — the withdrawal reaches completed when all funds have been delivered.

Preview a withdrawal

Before initiating, preview what balance is available for a given destination.
curl -X POST "$BASE_URL/v2/wallets/$WALLET_ID/withdrawal-preview" \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "destinationChain": "polygon",
    "amountUsd": 65000
  }'
FieldRequiredDescription
destinationChainYesDestination chain for the withdrawal
amountUsdNoAmount to preview as a number (max 6 decimal places). If omitted, previews the maximum available balance.
{
  "amountRequestedUsd": "65000.000000",
  "feeUsd": "0.000000",
  "withdrawableUsd": "82500.000000",
  "totalUsdAfterWithdrawal": "35000.000000",
  "estimatedCompletionTime": "PT2H"
}
FieldDescription
amountRequestedUsdThe withdrawal amount previewed (equals the requested amount, or the max available if amountUsd was omitted)
feeUsdFee for the withdrawal (currently always "0.000000")
withdrawableUsdConservative amount that can safely start a new withdrawal for this destination right now
totalUsdAfterWithdrawalEstimated wallet total after the previewed withdrawal amount is paid out
estimatedCompletionTimeEstimated end-to-end completion time as an ISO 8601 duration
withdrawableUsd on the wallet object and in preview are related but different:
  • Wallet withdrawableUsd is the wallet-level conservative amount that can be withdrawn now.
  • Preview withdrawableUsd is the destination-specific amount that can safely start right now.
  • Preview withdrawableUsd can be lower than the wallet object’s withdrawableUsd.
Ground keeps wallet-level ownership, wallet-level readiness, and destination-specific readiness separate on purpose. Value can remain customer-owned while still being unavailable to start a fresh withdrawal to a specific destination, for example when a prior unwind, bridge, or payout boundary is still settling and the destination representation has not yet been observed.

Initiate a withdrawal

curl -X POST "$BASE_URL/v2/wallets/$WALLET_ID/withdrawals" \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "requestId": "df8b7be6-e110-4f6d-9b2d-7c44a5b1f0b0",
    "destinationChain": "ethereum",
    "amountUsd": 65000,
    "destinationAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"
  }'
FieldRequiredDescription
requestIdYesUUID v4 idempotency key
destinationChainYesDestination chain
amountUsdYesAmount as a number (max 6 decimal places)
destinationAddressYesRecipient address on the destination chain
Withdrawals always deliver USDC to the destination address. There is no token field — USDC is the only supported withdrawal token. When withdraw succeeds, Ground has accepted the request and reserved liquidity against the wallet. The requested amountUsd remains the target amount for the withdrawal, but the payout legs are the source of truth for what was actually delivered. Integrators should set end-user expectations based on the underlying yield source, especially for asynchronous exits. Sandbox notes:
  • In sandbox, use explicit testnet keys such as destinationChain: "ethereum_sepolia".
  • destinationAddress must match the selected chain format: EVM hex for EVM chains, base58 for solana.

List withdrawals

curl -X GET "$BASE_URL/v2/wallets/$WALLET_ID/withdrawals?limit=25" \
  -H "Authorization: Bearer $API_TOKEN"
Query parameters:
ParamDescription
limitMax items (default 50, max 200)
cursorOpaque cursor from nextCursor
sortSort field (default createdAt)
orderdesc (default) or asc
createdAtGteISO-8601 timestamp filter
statusRepeatable status filter (e.g. status=processing&status=completed)

Fetch a withdrawal

curl -X GET "$BASE_URL/v2/wallets/$WALLET_ID/withdrawals/$WITHDRAWAL_ID" \
  -H "Authorization: Bearer $API_TOKEN"
{
  "id": "11b17950-1f5c-4d36-8f0d-0f3d1d0c6a45",
  "amountRequestedUsd": "65000.000000",
  "amountPaidUsd": null,
  "feeUsd": "0.000000",
  "destinationChain": "ethereum",
  "destinationAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
  "destinationToken": "usdc",
  "status": "processing",
  "legsCompleted": 0,
  "legsTotal": 1,
  "payoutLegs": [
    {
      "status": "pending_customer_approval",
      "amountUsd": "65000.000000",
      "from": { "kind": "yield_source", "id": "morpho-gauntlet-usdc", "label": "Morpho Gauntlet USDC Prime" },
      "to": { "kind": "external_payout", "id": "external_payout:ethereum:usdc", "label": "External payout (Ethereum)" },
      "startedAt": "2026-02-05T11:01:00.000Z",
      "completedAt": null,
      "stepsCompleted": 1,
      "stepsTotal": 2,
      "steps": [
        { "label": "Redeeming from Morpho Gauntlet USDC Prime", "status": "completed", "txHash": "0xdef456...", "txStatus": "confirmed" },
        { "label": "Sending payout", "status": "pending_customer_approval", "txHash": null, "txStatus": null }
      ]
    }
  ],
  "failureReason": null,
  "createdAt": "2026-02-05T11:00:00.000Z",
  "completedAt": null
}
Top-level fields:
FieldDescription
amountRequestedUsdThe requested withdrawal amount (the target).
amountPaidUsdSum of completed payout legs; null until at least one leg completes.
feeUsdTotal fee across legs (currently always "0.000000").
destinationChain / destinationAddressWhere funds are delivered.
destinationTokenAlways usdc.
statusWithdrawal status (see Withdrawal statuses).
legsCompleted / legsTotalProgress across payout legs.
payoutLegs[]The individual payout legs of the withdrawal (see below).
failureReasonPopulated when status is failed.
The payoutLegs array contains the individual legs of the withdrawal. Each leg moves funds from one representation (from) to another (to) through one or more onchain steps:
FieldDescription
statusLeg status (see complete list below)
amountUsdAmount routed through this leg, as a fixed-precision USD string
from / to{ kind, id, label } source and destination of the leg (e.g. yield_sourceexternal_payout)
startedAt / completedAtLeg timing
stepsCompleted / stepsTotalProgress across the leg’s onchain steps
steps[]Per-step detail: { label, status, txHash, txStatus }. txHash is populated after broadcast.
Per-leg Turnkey approval details (turnkeyActivityId, approvalRequestedAt, approvalValidBefore) are delivered via the portfolio_wallet.withdrawal.payout.status_changed webhook while a leg awaits customer approval — see Transaction Approvals.

Payout statuses

Withdrawal payoutLegs[].status and payoutLegs[].steps[].status use the workflow status set:
StatusTerminalMeaning
createdNoPayout leg or step has been planned but has not started.
processingNoPayout is actively being processed.
pending_customer_approvalNoAwaiting customer signature via Turnkey. Poll /turnkey/activities/pending or use the approvals UI/API to retrieve the activity details.
completedYesPayout delivered successfully; step txHash is populated when available.
failedYesPayout failed; see failureReason
cancelledYesPayout was cancelled (e.g. parent withdrawal cancelled)

Withdrawal statuses

Withdrawals move through five public statuses:
StatusMeaning
processingAccepted and in progress: unwinding positions, bridging, or delivering funds
partially_completedTerminal: some payout legs completed while others did not
completedTerminal: funds delivered to destination address
failedTerminal: withdrawal failed
cancelledTerminal: withdrawal was cancelled

Timing expectations

Withdrawal time depends on which yield positions need to unwind and whether cross-chain bridging is required.
PositionTypical timingDeveloper noteCustomer-facing copy
Cash (USDC)InstantNo unwind needed.This withdrawal is expected to complete right away.
morpho-gauntlet-usdcInstant (PT0H)Synchronous ERC-4626 redeem on Ethereum. There is no normal queue-based delay.This withdrawal is expected to complete quickly and is not normally queued.
morpho-steakhouse-usdcInstant (PT0H)Synchronous ERC-4626 redeem on Ethereum. There is no normal queue-based delay.This withdrawal is expected to complete quickly and is not normally queued.
syrup-usdcUp to 24h (PT24H)Queue-based withdrawal. Low-liquidity periods can take longer. The payout can be processed in parts, and the final amount is based on Maple’s withdrawal value when the queue processes the request.This withdrawal is queued rather than instant. Most withdrawals are processed within 24 hours, but it can take longer in low-liquidity periods. The payout can be processed in parts, and the final amount can be lower if Maple’s withdrawal value falls before processing.
Cross-chain delivery (CCTP) adds time on top of the unwind. Typical CCTP bridging is ~1 hour (p50), up to ~3 hours (max). For precise per-position estimates to a specific destination, use the withdrawal preview endpoint.

Withdrawal webhooks

Subscribe to portfolio_wallet.withdrawal.status_changed for real-time withdrawal tracking. See Webhooks for registration and payload details.

Bridge domains reference

A bridge domain is a hard boundary for where liquidity can come from. The withdrawal engine never crosses bridge domain boundaries.

USDC unified (CCTP)

USDC on CCTP-supported chains forms a single usdc:unified domain:
  • Arbitrum, Base, Ethereum, Polygon, Solana
A withdrawal to any of these chains can source USDC from any other chain in the domain (via CCTP bridging).

How cash fits in

Within a bridge domain, cash is always the first source of liquidity. Only if cash is insufficient will the system unwind yield positions in the same domain. Bridge domain IDs are always lowercase and intended to be stable, but use the withdrawal preview as the canonical source of truth for what’s available.