Create wallet

Provisions a new wallet under exactly one provider account: the project's Managed config for provider, or the Custody Connection named by connectionId.

POST/v1/walletsRun in dashboard

A body naming both, or neither, is rejected with 400.

With an Idempotency-Key, SDP never records a second wallet for one key: a retry returns the wallet an earlier attempt recorded, even after the 24-hour key window. A provider that accepts a request id also receives one derived from the key.

On a provider that dedupes on the request id, a 5xx is not stored: a same-key retry runs again and gets back the wallet the first attempt made, even when SDP failed to record it, for as long as the provider keeps the request id (permanently, or for 24 hours; the Idempotency guide lists each provider). On any other provider a failure is stored as a 503, and a new key is a new provider request. A 503 can mean the provider created the wallet before the error. With a provider that accepts no request id, a failure that left no SDP wallet row makes even the same key a new provider request after 24 hours. Before any retry after an ambiguous failure, whatever the key, check GET /v1/wallets and, if the wallet is not there, the provider account.

The provider request id and the recorded-wallet lookup both include the credential. After an API key rotation, once the key's 24-hour record has expired, the same key under the new credential is a new provider request and can create a second wallet: list wallets with GET /v1/wallets and check the provider account before retrying a create.

Requires an API key with the wallets.manage action.

Headers

  • x-project-id · string · min length 1 · example prj_example

    Required for dashboard sessions. API keys use their assigned project.

  • Idempotency-Key · string

    Use a unique 1–255 printable ASCII key per operation. Reuse it with the same request on retries. An in-flight attempt returns 409; a changed request returns 422. Retry and recovery rules.

Request body

application/json · required

Create wallet request body. Names exactly one owner: provider (Managed config) or connectionId (Custody Connection); a body naming both, or neither, is rejected with 400.

Any of:

  • Create a wallet under the project's Managed config for provider. · must not include connectionId

    • provider · string · required · one of fireblocks, privy, coinbase_cdp, para, turnkey, dfns, ibm_haven, anchorage, utila · example privy

      Provider whose Managed config owns the new wallet. A project holds one active config per provider.

    • label · string · max length 100 · example Mint authority wallet

      Optional label for the new wallet.

    • purpose · string · one of transfer, fee_payer · default transfer

      What the wallet is for. Defaults to transfer, the only purpose a payment, transfer batch or recurring payment can send from.

  • Create a wallet under a Custody Connection. · must not include provider

    • connectionId · string · required · min length 1 · example cconn_123

      Custody Connection (BYOK) that owns the new wallet.

    • label · string · max length 100 · example Mint authority wallet

      Optional label for the new wallet.

    • purpose · string · one of transfer, fee_payer · default transfer

      What the wallet is for. Defaults to transfer, the only purpose a payment, transfer batch or recurring payment can send from.

Responses

  • 201 Wallet created

    • data · object · required

      Created wallet response payload.

      • wallet · object · required

        Wallet details.

        • id · string · required · example cwlt_example

          Wallet record ID.

        • custodyConfigId · string · example cfg_example

          Owning custody configuration ID.

        • custodyConnectionId · string · example cconn_example

          Owning Custody Connection ID.

        • provider · string · example privy

          Wallet signing provider.

        • isRuntimeExecutionAllowed · boolean · required · example true

          Whether SDP currently permits attempting runtime execution through this wallet's owner. This is request-time admission, not Provider health or a success guarantee.

        • walletId · string · required · min length 1 · example privy_wallet_123

          Provider wallet ID.

        • publicKey · string · required · min length 32 · max length 44 · example So11111111111111111111111111111111111111112

          Base58-encoded Solana address.

        • label · string | null · required · example Treasury wallet

          Optional wallet label.

        • purpose · string | null · required · example transfer

          Optional wallet purpose.

        • status · string · required · one of active, inactive · example active

          Wallet status.

        • createdAt · string · required · format date-time · example 2025-01-01T00:00:00.000Z

          ISO 8601 timestamp.

        • balances · object[]

          Tracked token balances when requested and successfully observed. Omitted when the balance lookup is unavailable; an empty array is a successful zero-token result.

          • token · string · required · example USDC

            Tracked token symbol.

          • mint · string · required · min length 32 · max length 44 · example 4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU

            Tracked token mint address.

          • amount · string · required · example 1250000

            Raw token amount as a string.

          • uiAmount · string · required · example 1.25

            Human-readable token balance.

          • decimals · integer · required · min 0 · example 6

            Token decimals.

          • usdPrice · number · example 1

            Resolved USD price per token when available.

          • usdValue · number · example 125.5

            Resolved USD value of this balance when pricing is available.

        One of:

        • Owned by a Managed config. · must include custodyConfigId · must not include custodyConnectionId

        • Owned by a Custody Connection. · must include custodyConnectionId · must not include custodyConfigId

    • meta · object · required

      Response metadata.

      • requestId · string · required · min length 1 · example req_example

        Request identifier for tracing.

      • timestamp · string · required · format date-time · example 2025-01-01T00:00:00.000Z

        ISO 8601 timestamp.

  • 400 Bad Request

    • error · object · required

      Standard error payload.

      • code · string · required

        Machine-readable error code.

      • message · string · required

        Human-readable error message.

      • details · object

        Optional error details for debugging.

        • * · any
    • meta · object

      • requestId · string · min length 1 · example req_example

        Request identifier for tracing.

  • 401 Unauthorized

    Same fields as 400.

  • 403 Forbidden. Requires the wallets.manage action: an API key must hold it (a module's manage or execute also covers its read), and a signed-in member's organization role must include it. Without it the error is INSUFFICIENT_PERMISSIONS and error.details.action names the action. Any other 403 names its own reason. Forbidden: the caller lacks the route's action or the API key is wallet-scoped, or the provider account was refused. A refusal's details.reason names the failed check: custody_mode_not_allowed when provider names a Managed config in a Production project (Production is BYOK only: create the wallet in a Custody Connection with connectionId), custody_provider_not_in_release_channel, or provider_not_entitled.

    Same fields as 400.

  • 404 Not Found

    Same fields as 400.

  • 409 Conflict

    Same fields as 400.

  • 422 Unprocessable Entity

    Same fields as 400.

  • 500 Internal Server Error

    Same fields as 400.

  • 503 Service Unavailable

    Same fields as 400.

Example request

IDEMPOTENCY_KEY=$(uuidgen)
curl -X POST 'https://api-beta.solana.com/v1/wallets' \
  -H "Authorization: Bearer $SDP_API_KEY" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "privy",
    "label": "Treasury wallet",
    "purpose": "transfer"
  }'
// Reuse this key when you retry this request.
const idempotencyKey = crypto.randomUUID();
const res = await fetch("https://api-beta.solana.com/v1/wallets", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SDP_API_KEY}`,
    "Idempotency-Key": idempotencyKey,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "provider": "privy",
    "label": "Treasury wallet",
    "purpose": "transfer"
  })
});
import os, requests, uuid

# Reuse this key when you retry this request.
idempotency_key = str(uuid.uuid4())
r = requests.post("https://api-beta.solana.com/v1/wallets",
  headers={"Authorization": f"Bearer {os.environ['SDP_API_KEY']}", "Idempotency-Key": idempotency_key},
  json={
    "provider": "privy",
    "label": "Treasury wallet",
    "purpose": "transfer"
  })

Example responses

JSON
{
  "data": {
    "wallet": {
      "id": "cwlt_example",
      "custodyConfigId": "cfg_example",
      "provider": "privy",
      "isRuntimeExecutionAllowed": true,
      "walletId": "privy_wallet_123",
      "publicKey": "So11111111111111111111111111111111111111112",
      "label": "Treasury wallet",
      "purpose": "transfer",
      "status": "active",
      "createdAt": "2025-01-01T00:00:00.000Z"
    }
  },
  "meta": {
    "requestId": "req_example",
    "timestamp": "2025-01-01T00:00:00.000Z"
  }
}
JSON
{
  "error": {
    "code": "INSUFFICIENT_PERMISSIONS",
    "message": "Missing action: wallets.manage",
    "details": {
      "action": "wallets.manage"
    }
  },
  "meta": {
    "requestId": "req_example"
  }
}

Error responses use the standard error shape.