Skip to main content
This quickstart walks through listing available yield sources, creating a Portfolio Wallet, funding it via the returned deposit addresses, checking balances, and withdrawing.

Prerequisites

Set your base URL and API token once, then reuse them for every request. Use sandbox while you build, then switch to production without changing request shapes.
cURL
export BASE_URL="https://sandbox.groundtech.co"
export GROUND_API_KEY="your_api_token"

Conventions

  • JSON field names are camelCase.
  • Enum-like string values (for example status, type, and webhook event/eventTypes) are lower_snake_case.
    • Example: withdrawal.status = pending_customer_approval

1. List available yield sources

Fetch the yield sources you can allocate to. Each yield source has a positionKey you’ll use when creating a wallet.
cURL
curl -X GET "$BASE_URL/v2/wallets/yield-sources" \
  -H "Authorization: Bearer $GROUND_API_KEY"
The response contains a yieldSources array. Note the positionKey and currentRateBps for each source to decide your allocation.

2. Create a portfolio wallet

Create a Portfolio Wallet by passing positions with your chosen yield allocation. Positions must sum to 10,000 bps (100%).
cURL
curl -X POST "$BASE_URL/v2/wallets" \
  -H "Authorization: Bearer $GROUND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "requestId": "123e4567-e89b-12d3-a456-426614174000",
    "label": "Treasury Portfolio",
    "positions": [
      { "positionKey": "resolv_rlp", "targetWeightBps": 4000 },
      { "positionKey": "tbills", "targetWeightBps": 6000 }
    ]
  }'
The response includes the wallet id and depositAddresses per chain. Save the wallet id for the remaining steps:
cURL
export WALLET_ID="<wallet_id_from_create_response>"

3. Deposit actual funds

Send a stablecoin transfer from your custody to the chain you want to fund.
Plain
to:    <depositAddresses.arbitrum>   (the 0x address from the create response)
chain: arbitrum
token: usdc
amount: 50,000.00

4. Await Deposit Confirmation

Deposits are detected on-chain and then processed. You can track the latest deposit status either by polling the deposits endpoints or by subscribing to webhooks. Poll (REST):
  • List deposits for a wallet: GET /v2/wallets/{id}/deposits
  • Fetch a single deposit: GET /v2/wallets/{id}/deposits/{depositId}
Example:
cURL
curl -X GET "$BASE_URL/v2/wallets/$WALLET_ID/deposits?limit=25" \
  -H "Authorization: Bearer $GROUND_API_KEY"
Webhook events:
  • portfolio_wallet.deposit.status_changed
Possible deposit statuses (deposit.status):
  • processing
  • completed
  • failed
Per-leg status detail is available in deposit.legStatuses (keys vary by strategy/position). Possible leg statuses:
  • pending
  • processing
  • completed
  • skipped
  • failed

5. Fetch the updated balance

Fetch the wallet to see current balances after the deposit is processed. Key fields in the response:
  • totalBalanceUsd — net assets (gross minus pending withdrawal reservations)
  • grossAssetsUsd — total value across all positions and cash
  • breakdownUsd — breakdown into cashUsd, inTransitUsd, positionClaimsUsd
cURL
curl -X GET "$BASE_URL/v2/wallets/$WALLET_ID" \
  -H "Authorization: Bearer $GROUND_API_KEY"

6. Withdraw (including the signing flow)

Ground uses Turnkey to manage signing flows, but you do not need a relationship with Turnkey to sign approvals.
Initiate a withdrawal. The response includes a turnkeyActivityId if approval is required.
cURL
curl -X POST "$BASE_URL/v2/wallets/$WALLET_ID/withdraw" \
  -H "Authorization: Bearer $GROUND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "requestId": "df8b7be6-e110-4f6d-9b2d-7c44a5b1f0b0",
    "chain": "ethereum",
    "token": "usdc",
    "withdrawalAmount": 65000,
    "destinationAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"
  }'
Save the id from the response (this is the withdrawal id used for status checks):
cURL
export WITHDRAWAL_ID="<withdrawal_id_from_withdraw_response>"
Customer-side approval example (Turnkey):
Signing keys are provisioned and managed in your Ground Portal account. Ensure you always store signing keys safely as lost or compromised keys could result in lost funds.
Node
import { Turnkey } from "@turnkey/sdk-server";

// Mock keypair for docs only. Replace with your real customer signer key.
const TURNKEY_API_PUBLIC_KEY = "04aa...mock_public_key...ff";
const TURNKEY_API_PRIVATE_KEY = "11bb...mock_private_key...ee";
const TURNKEY_SUB_ORGANIZATION_ID = "your_turnkey_sub_organization_id";

const turnkey = new Turnkey({
  apiBaseUrl: "https://api.turnkey.com",
  defaultOrganizationId: TURNKEY_SUB_ORGANIZATION_ID,
  apiPublicKey: TURNKEY_API_PUBLIC_KEY,
  apiPrivateKey: TURNKEY_API_PRIVATE_KEY,
});

const apiClient = turnkey.apiClient();

export async function approveTurnkeyActivity(turnkeyActivityId) {
  // 1) Fetch the activity to get its fingerprint
  const { activity } = await apiClient.getActivity({
    organizationId: TURNKEY_SUB_ORGANIZATION_ID,
    activityId: turnkeyActivityId,
  });

  const fingerprint = activity?.fingerprint;
  if (!fingerprint) {
    throw new Error("Turnkey activity fingerprint missing");
  }

  // 2) Approve it (this registers your approval vote in Turnkey)
  await apiClient.approveActivity({
    type: "ACTIVITY_TYPE_APPROVE_ACTIVITY",
    timestampMs: String(Date.now()),
    organizationId: TURNKEY_SUB_ORGANIZATION_ID,
    parameters: { fingerprint },
  });
}

// Usage:
// const { id: withdrawalId, turnkeyActivityId } = withdrawalResponse;
// if (turnkeyActivityId) await approveTurnkeyActivity(turnkeyActivityId);

7. Await Withdrawal Confirmation

After approval (if required), the withdrawal is kicked off automatically. You can track the latest withdrawal status either by polling the withdrawal endpoint or by subscribing to webhooks. Poll (REST):
cURL
curl -X GET "$BASE_URL/v2/wallets/$WALLET_ID/withdrawals/$WITHDRAWAL_ID" \
  -H "Authorization: Bearer $GROUND_API_KEY"
Webhook events:
  • portfolio_wallet.withdrawal.status_changed
  • portfolio_wallet.withdrawal.payout.status_changed (per-leg payouts)
Possible withdrawal statuses (withdrawal.status):
  • pending_liquidity
  • processing
  • pending_customer_approval
  • pending_broadcast
  • broadcasted
  • completed
  • partially_completed
  • failed
  • cancelled
For more detail, see Turnkey Approvals and the API Reference withdrawal endpoints.