Skip to main content
This quickstart walks through generating a quote, creating a Portfolio Wallet from the quote’s positions, funding it via the returned deposit addresses, checking balances and yield, and withdrawing with the signing flow.

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.trybraid.xyz"
export BRAID_API_TOKEN="your_api_token"

1. Create a quote with desired parameters

Use quotes to design a strategy with liquidity constraints or minimum APY targets. Scenario (liquidity-first): you want a wallet where at least 40% of funds are instantly withdrawable, and the remainder can be placed into higher-yield positions that may take longer to unwind. To express that, add a liquidity constraint requiring at least 4000 bps to have maxProcessingTime: PT0H (0 hours).
cURL
curl -X POST "$BASE_URL/v2/portfolio-wallets/quote" \
  -H "Authorization: Bearer $BRAID_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "requestId": "b7c4ad36-0b77-4b3a-8c9a-1ab6f0b2f98a",
    "minApyTargetBps": 500,
    "liquidityConstraints": [
      { "maxProcessingTime": "PT0H", "minWeightBps": 4000 }
    ],
    "yieldSourceTypeExclusions": ["deltaNeutralStrategy"]
  }'
The response is an array of quote options. Pick the option you want (often quote[0] when constraintsSatisfied is true), then use its strategyConfig.positions to create a wallet.

2. Create a portfolio wallet from the quote

Create a Portfolio Wallet by passing the chosen quote positions as positions. Positions must sum to 10,000 bps (100%).
cURL
curl -X POST "$BASE_URL/v2/portfolio-wallets" \
  -H "Authorization: Bearer $BRAID_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "requestId": "123e4567-e89b-12d3-a456-426614174000",
    "label": "Treasury Portfolio",
    "positions": [
      { "positionKey": "tbills", "targetWeightBps": 6000 },
      { "positionKey": "resolvRlp", "targetWeightBps": 4000 }
    ]
  }'
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[chain].address>
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/portfolio-wallets/{id}/deposits
  • Fetch a single deposit: GET /v2/portfolio-wallets/{id}/deposits/{depositId}
Example:
cURL
curl -X GET "$BASE_URL/v2/portfolio-wallets/$WALLET_ID/deposits?pageSize=25" \
  -H "Authorization: Bearer $BRAID_API_TOKEN"
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 and yield accrual

Fetch the wallet to get the current balance and yield metrics.
  • Current total balance: use GET /v2/portfolio-wallets/{id}totalBalanceUsd
  • Yield earned: POST /v2/portfolio-wallets/yieldportfolioWallets[$WALLET_ID].accruedDelta (or global.accruedDelta if you omit portfolioWalletIds)
cURL
curl -X GET "$BASE_URL/v2/portfolio-wallets/$WALLET_ID" \
  -H "Authorization: Bearer $BRAID_API_TOKEN"

curl -X POST "$BASE_URL/v2/portfolio-wallets/yield" \
  -H "Authorization: Bearer $BRAID_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"periodStartIso\": \"2026-01-01T00:00:00Z\",
    \"periodEndIso\": \"2026-02-01T00:00:00Z\",
    \"portfolioWalletIds\": [\"$WALLET_ID\"]
  }"

6. Withdraw (including the signing flow)

Braid 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/portfolio-wallets/$WALLET_ID/withdraw" \
  -H "Authorization: Bearer $BRAID_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "requestId": "df8b7be6-e110-4f6d-9b2d-7c44a5b1f0b0",
    "destinationAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
    "withdrawalAmount": 65000,
    "destinationToken": "usdc",
    "destinationChain": "ethereum"
  }'
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 Braid 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/portfolio-wallets/$WALLET_ID/withdrawals/$WITHDRAWAL_ID" \
  -H "Authorization: Bearer $BRAID_API_TOKEN"
Webhook events:
  • portfolio_wallet.withdrawal.status_changed
  • portfolio_wallet.withdrawal.payout.status_changed (per-leg payouts)
Possible withdrawal statuses (withdrawal.status):
  • pending_approval
  • created
  • processing
  • completed
  • failed
For more detail, see Get portfolio withdrawal, portfolio-yield-wallets/turnkey-approvals, and the v2 API Reference withdrawal endpoints.