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

# Create a Wallet

> Create a portfolio wallet and poll for activation.

Creating a wallet is asynchronous. `POST /v2/wallets` returns immediately with `status: "creating"`. Deposit addresses and on-chain state are not populated synchronously — poll `GET /v2/wallets/{id}` until `status` becomes `"idle"`.

## Create a portfolio wallet

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -X POST "$BASE_URL/v2/wallets" \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "requestId": "0b7e3b4d-88a5-4a75-8c0f-3b7b4f9d9f1d",
    "label": "Core Yield Portfolio",
    "strategy": {
      "allocations": {
        "usdc": [
          { "yieldSourceId": "syrup-usdc", "pct": 50 },
          { "yieldSourceId": "morpho-gauntlet-usdc", "pct": 50 }
        ],
        "usdt": [
          { "yieldSourceId": "cash", "pct": 100 }
        ]
      }
    }
  }'
```

| Field                  | Required | Description                                                                    |
| ---------------------- | -------- | ------------------------------------------------------------------------------ |
| `requestId`            | Yes      | UUID v4 idempotency key                                                        |
| `label`                | No       | Human-readable wallet name                                                     |
| `strategy`             | Yes      | Object containing `allocations`                                                |
| `strategy.allocations` | Yes      | An object keyed by `usdc` and/or `usdt`. Each included array must sum to 100%. |

Place each yield source under the token it accepts, as shown by its `depositToken` in `GET /v2/wallets/yield-sources`. Use `cash` to keep funds uninvested in that token. If you omit USDC or USDT when creating a wallet, that token defaults to 100% cash.

A first POST with a new `requestId` returns **`200 OK`** with a minimal creating envelope (no deposit addresses, zero balances). An idempotent replay with the same `requestId` **and an identical payload** returns **`200 OK`** with the wallet's current state. Reusing a `requestId` with a **divergent payload** (different label or allocations) returns **`409 request_id_conflict`** — use a fresh `requestId` for a genuinely new wallet. See [Idempotency](/docs/portfolio-wallets/api-conventions#idempotency) for the full comparison rules.

## Creating response

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "9d1a1c83-3a1c-4c14-9c5a-0c9a57a4a7db",
  "label": "Core Yield Portfolio",
  "status": "creating",
  "failureReason": null,
  "createdAt": "2026-02-05T08:15:00Z",
  "depositAddresses": {},
  "balance": {
    "totalUsd": "0.000000",
    "withdrawableUsd": "0.000000",
    "reservedUsd": "0.000000",
    "earnedUsd": "0.000000"
  },
  "positions": [],
  "strategyAllocations": {
    "usdc": [
      { "yieldSourceId": "syrup-usdc", "targetWeightBps": 5000 },
      { "yieldSourceId": "morpho-gauntlet-usdc", "targetWeightBps": 5000 }
    ],
    "usdt": [
      { "yieldSourceId": "cash", "targetWeightBps": 10000, "token": "usdt", "chain": "ethereum" }
    ]
  }
}
```

## Polling for activation

Poll `GET /v2/wallets/{id}` every 1-2 seconds until `status !== "creating"`:

```javascript theme={"theme":{"light":"github-light","dark":"github-dark"}}
async function waitUntilReady(walletId, apiToken) {
  while (true) {
    const res = await fetch(`${BASE_URL}/v2/wallets/${walletId}`, {
      headers: { Authorization: `Bearer ${apiToken}` },
    });
    const wallet = await res.json();
    if (wallet.status === "idle") return wallet;
    if (wallet.status === "failed") {
      throw new Error(`wallet ${walletId} failed: ${wallet.failureReason}`);
    }
    await new Promise((r) => setTimeout(r, 1500));
  }
}
```

See [Polling](/docs/portfolio-wallets/polling) for more on the poll pattern.

## Ready response

Once `status === "idle"`, the response is ready for deposits:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "9d1a1c83-3a1c-4c14-9c5a-0c9a57a4a7db",
  "label": "Core Yield Portfolio",
  "status": "idle",
  "failureReason": null,
  "createdAt": "2026-02-05T08:15:00Z",
  "depositAddresses": {
    "arbitrum": "0x21246509968c4d24611f414560971AEc2e3A079B",
    "base": "0x21246509968c4d24611f414560971AEc2e3A079B",
    "ethereum": "0x21246509968c4d24611f414560971AEc2e3A079B",
    "polygon": "0x21246509968c4d24611f414560971AEc2e3A079B",
    "solana": "7nYzKxM3bP4oEFbqkPmA5E2rYJ8HqKz8vFg9abc1"
  },
  "balance": {
    "totalUsd": "0.000000",
    "withdrawableUsd": "0.000000",
    "reservedUsd": "0.000000",
    "earnedUsd": "0.000000"
  },
  "positions": [
    {
      "id": "syrup-usdc",
      "kind": "yield_source",
      "label": "Syrup USDC",
      "valueUsd": "0.000000",
      "yieldSourceId": "syrup-usdc",
      "pct": 50
    },
    {
      "id": "morpho-gauntlet-usdc",
      "kind": "yield_source",
      "label": "Morpho Gauntlet USDC Prime",
      "valueUsd": "0.000000",
      "yieldSourceId": "morpho-gauntlet-usdc",
      "pct": 50
    }
  ],
  "strategyAllocations": {
    "usdc": [
      { "yieldSourceId": "syrup-usdc", "targetWeightBps": 5000 },
      { "yieldSourceId": "morpho-gauntlet-usdc", "targetWeightBps": 5000 }
    ],
    "usdt": [
      { "yieldSourceId": "cash", "targetWeightBps": 10000, "token": "usdt", "chain": "ethereum" }
    ]
  }
}
```

### Key fields

| Field                     | Description                                                                                                                                                                                     |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`                  | `"creating"` during provisioning, `"idle"` when ready, `"withdrawal_active"` / `"rebalance_active"` / `"withdrawal_and_rebalance_active"` while work is running, `"failed"` if setup terminated |
| `failureReason`           | Populated only when `status === "failed"` — short human-readable reason                                                                                                                         |
| `depositAddresses`        | Per-chain addresses to fund the wallet. Empty until `status === "idle"`.                                                                                                                        |
| `balance.totalUsd`        | Total economically owned wallet value                                                                                                                                                           |
| `balance.withdrawableUsd` | Amount that can be withdrawn now                                                                                                                                                                |
| `balance.reservedUsd`     | Customer-owned value currently reserved by active withdrawals or rebalances                                                                                                                     |
| `balance.earnedUsd`       | Lifetime yield earned (monotonic)                                                                                                                                                               |
| `positions`               | Current cash and yield-source holdings with USD values. Yield-source positions include `yieldSourceId` and `pct`; configured cash allocations may also include `pct`.                           |
| `strategyAllocations`     | Desired targets grouped by stablecoin. Target weights use basis points and each returned group totals 10,000.                                                                                   |

## Failed response

If wallet setup fails terminally, `GET /v2/wallets/{id}` returns:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "id": "9d1a1c83-3a1c-4c14-9c5a-0c9a57a4a7db",
  "label": "Core Yield Portfolio",
  "status": "failed",
  "failureReason": "Turnkey create_wallet activity ACTIVITY_STATUS_REJECTED: act_abc",
  "createdAt": "2026-02-05T08:15:00Z",
  "depositAddresses": {},
  "balance": { "totalUsd": "0.000000", "withdrawableUsd": "0.000000", "reservedUsd": "0.000000", "earnedUsd": "0.000000" },
  "positions": []
}
```

Failed wallets cannot be recovered by retrying the same `requestId`. Create a new wallet with a fresh `requestId` and contact support if the failure reason is unclear.

## Rebalancing

Portfolio wallets are best-effort targets. Over time, or after deposits and withdrawals, holdings drift from target allocations. The system rebalances to bring positions back toward the strategy targets. Deposits first appear as cash and are then deployed into the configured yield sources.

## Sandbox notes

In sandbox, deposit address keys include the network suffix (e.g. `ethereum_sepolia` instead of `ethereum`). Sandbox USDT uses a Ground-owned mock contract. See [Supported Chains](/docs/portfolio-wallets/supported-chains) for details.
