> ## Documentation Index
> Fetch the complete documentation index at: https://docs.conduit.financial/llms.txt
> Use this file to discover all available pages before exploring further.

# Crypto Wallets

> On-chain non-custodial wallets for receiving and sending crypto

## Overview

Each of your customers gets **Crypto Wallets** of their own: keys they control, ready to receive, hold, and send crypto across the supported chains. Network fees are sponsored, so a wallet never needs a native-coin balance.

* **Receive**: share the wallet's `address`; deposits are detected on-chain, screened, and credited to `balances[]`, with a webhook at each step.
* **Hold**: per-asset balances, split into `available`, `pending`, and `frozen`.
* **Send**: payouts and conversions leave only after the customer's signers approve. Who those signers are (people with passkeys, your backend with server-held keys, or a mix) is the customer's [signing mode](/concepts/signing-modes).

Each wallet lives on one `chain` with one on-chain `address`, and it is scoped to a customer. No wallets are created during onboarding; they are provisioned when the customer claims non-custodial control (see [Creating Wallets](#creating-wallets)), which by default provisions a wallet on the default set of supported chains in one step, or on the specific chains you request.

<Note>
  **Only receiving crypto to fund a conversion?** You may not need customer
  wallets at all: a [deposit-funded order](/concepts/deposit-funded-orders)
  returns a funding address per order, with no claim, no signer roster, and no
  co-signature.
</Note>

## Custody

Wallets provisioned on this surface are **non-custodial**: the customer holds the signing keys, and moving funds out takes both the customer's approval and Conduit's compliance co-signature; neither side can act alone. Conduit broadcasts once the customer's signing threshold is met; it never holds the customer's keys.

Receiving crypto works as soon as a wallet exists. The co-signing that governs **outbound** transfers is covered in [Non-Custodial Wallets](/concepts/non-custodial-wallets).

### Signing modes

Signers don't have to be humans. Every customer has a **signing mode** (passkey required, programmatic, or programmatic unattended) that decides whether payout approvals come from people with passkeys, from your servers, or a mix. Conduit sets it for each customer. See [Signing modes](/concepts/signing-modes) to choose between them, and [Programmatic payout signing](/guides/machine-signer-stamping) for the machine flow.

## The Wallet Resource

| Field                | Type           | Description                                                                                                                                         |
| -------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                 | string         | Wallet ID, prefixed `wlt_`.                                                                                                                         |
| `chain`              | enum           | The blockchain this wallet operates on.                                                                                                             |
| `address`            | string \| null | The on-chain address. `null` while the wallet is provisioning.                                                                                      |
| `status`             | enum           | Lifecycle status; see below.                                                                                                                        |
| `custodyModel`       | enum \| null   | `non_custodial` for wallets provisioned on this surface; `custodial` for legacy/operational wallets. Omitted until the custody model is determined. |
| `rotatedAt`          | string \| null | When this wallet was rotated, if it has been.                                                                                                       |
| `replacedByWalletId` | string \| null | The wallet that replaced this one after rotation.                                                                                                   |
| `clientReferenceId`  | string \| null | Your external reference, if you supplied one at creation.                                                                                           |
| `balances[]`         | array          | Per-asset balances, each with `available`, `pending`, and `frozen` amounts.                                                                         |
| `createdAt`          | string         | When the wallet was created.                                                                                                                        |
| `updatedAt`          | string         | When the wallet was last updated.                                                                                                                   |

### Balances

Each entry in `balances[]` splits a single asset into three buckets:

* **`available`**: settled funds you can move out.
* **`pending`**: funds observed on-chain but not yet fully settled.
* **`frozen`**: funds held and temporarily unavailable.

Balances reflect deposits as they arrive. See [Receiving crypto](#receiving-crypto) below.

### Status lifecycle

| Status     | Meaning                                                                                                                           |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `pending`  | The wallet has been requested and Conduit is provisioning its on-chain address. `address` is `null` until provisioning completes. |
| `active`   | The wallet has an address and can receive and send funds.                                                                         |
| `disabled` | The wallet is no longer in use; for example, it has been rotated and replaced.                                                    |

<Note>
  A wallet enters `pending` with a `null` `address` while it is being
  provisioned. Rather than polling, subscribe to
  [`crypto_wallet.completed`](/webhooks#crypto_walletcompleted) to learn when
  provisioning has finished and the `address` is populated.
</Note>

## Supported Chains

Wallets can be created on the following chains. The `chain` value is always lowercase.

| `chain`    | Network  |
| ---------- | -------- |
| `ethereum` | Ethereum |
| `base`     | Base     |
| `polygon`  | Polygon  |
| `solana`   | Solana   |
| `tron`     | Tron     |

The three EVM chains (`ethereum`, `base`, and `polygon`) share a single on-chain `address`. A customer's wallets on those chains all resolve to the same address, so funding one and reading another are the same account on each network. `solana` and `tron` each have their own distinct address.

<Note>
  `solana` is created only when you request it explicitly in `chains`; it is
  not part of the default chain set. `solana` wallets are address-only for now:
  you can create the wallet and receive on it, but outbound payouts and orders
  on Solana are not yet available. Payouts and orders are supported on the EVM
  chains and `tron` (`tron` carries USDT only). An order naming an unsupported
  asset/chain combination is refused with `UNSUPPORTED_ASSET`; a payout is
  refused as a validation error on `assetAmount.chain`. More generally, a chain
  being listed here (or a wallet being created for it) does not imply Conduit
  yet supports moving every asset on that chain.
</Note>

## Creating Wallets

Wallets are provisioned by claiming non-custodial control once per customer, which by default provisions a wallet on the default set of supported chains. Adding a wallet on a chain not in that set (or one you excluded from the claim) is an optional second step.

One prerequisite: the customer needs the `crypto_wallet` feature approved before it can claim. Requesting it and tracking the approval is Step 1 of [Add a Crypto Wallet](/guides/add-crypto-wallet).

<Steps>
  <Step title="Claim non-custodial control (once per customer)">
    Opt the customer into the non-custodial multi-signer model:

    ```http theme={null}
    POST /v2/customers/{customerId}/wallets/claim-non-custodial

    {
      "roster": [ ... ],
      "signingThreshold": 2
    }
    ```

    The request body may include an optional `chains` array naming which chains to provision. **Omit it**, the recommended default, and the default set of supported chains (`ethereum`, `base`, `polygon`, `tron`) is provisioned in one claim, so a customer that later needs a new chain does not have to re-authenticate its signers. Provisioning one wallet up front on each chain covers future use with no further signer ceremony. `solana` is not in the default set: name it in `chains` to provision it.

    Because the three EVM chains share a single address and key, provisioning them costs one admin approval, not three. `tron` is provisioned as its own address in the same claim.

    Returns `202`. The customer's signers complete their enrollment via per-signer verification URLs delivered through `wallet_signer.invited` webhooks. Once the final signer has enrolled, `crypto_wallet.completed` fires and the claim's wallets activate. Polling `GET /v2/customers/{customerId}/wallets/claims/{claimId}` returns a `chains` field listing every chain provisioned for the claim.
  </Step>

  <Step title="Add a wallet on another chain (optional)">
    When you omit `chains` at claim time, wallets on the default set of supported chains already exist and this step is unnecessary. To add a wallet on a chain not in that set (such as `solana`) or one you explicitly excluded, call:

    ```http theme={null}
    POST /v2/customers/{customerId}/wallets
    idempotency-key: <unique-key>

    { "chain": "base", "clientReferenceId": "ext-wallet-001" }
    ```

    `clientReferenceId` is optional: 1-255 characters from A-Za-z, 0-9, underscore, hyphen, colon, and period; no spaces. The `idempotency-key` header is required; replaying the same key returns the same result rather than creating a duplicate.

    The response has **two success shapes**, depending on whether the new wallet needs fresh signing material — plus a retryable `409` while that material is still being set up:

    * **`201 Created` with the wallet**: when the chain reuses key material the customer already controls. Adding an EVM chain (`ethereum`, `base`, or `polygon`) while the customer already has an EVM wallet reuses the existing shared address and key, so no admin approval is needed and the wallet is returned immediately, already `active` on the shared address.
    * **`202 Accepted` with `{ "ceremonyId": "wcm_…", "verificationUrl": "…" }`**: when the wallet needs new signing material (a chain that does not share the existing key, or a second wallet on a chain). This parks an admin-cosign ceremony rather than creating the wallet inline: route `verificationUrl` to one of the customer's admins to approve on the verify page. A [`wallet_ceremony.awaiting_admin_approval`](/webhooks#wallet_ceremonyawaiting_admin_approval) webhook carries the same admin link. On approval, [`wallet.created`](/webhooks#walletcreated) and `crypto_wallet.completed` fire and the wallet is `active`; if the admin declines, `wallet_ceremony.failed` fires and no wallet is created.
      Retrying while a ceremony for the same chain is already pending is idempotent: the call re-mints that pending ceremony's approval and returns `202` again. While the new signing material is still being set up there is no link to return yet, so the call is refused with a retryable `409 CEREMONY_IN_FLIGHT` — retry shortly and the `202` arrives once the material is ready. The same `409` is returned when a concurrent request for the same chain has not finished recording its ceremony. Wait for the pending ceremony to resolve rather than opening a second one.

    Before the customer has claimed non-custodial control, the same request returns `422 WALLET_NO_PROVIDER_ACCOUNT`. See [Non-Custodial Wallets](/concepts/non-custodial-wallets) for the multi-signer model and the [`WALLET_NO_PROVIDER_ACCOUNT`](/errors#wallet-no-provider-account) error page for the resolution path.
  </Step>
</Steps>

## Reading Wallets

| Endpoint                                            | Returns                                                                                            |
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `GET /v2/customers/{customerId}/wallets`            | A cursor-paginated list of the customer's wallets. Accepts an optional `clientReferenceId` filter. |
| `GET /v2/customers/{customerId}/wallets/{walletId}` | A single wallet.                                                                                   |

## Key Rotation

Rotating a wallet replaces its on-chain key. It produces a **new wallet** with a **new `address`**; the old wallet moves to `disabled`. The two are linked: the old wallet's `replacedByWalletId` points to the new wallet, and the new wallet carries the same `replacedByWalletId` back-reference so the chain of replacements is traceable.

```http theme={null}
POST /v2/customers/{customerId}/wallets/{walletId}/rotate
idempotency-key: <unique-key>
```

Returns `200` with the replacement wallet. The `idempotency-key` header is required.

<Warning>
  Move the funds out before rotating: the new key controls a different address,
  so rotation is blocked while the wallet still holds a balance worth moving. A
  leftover at or below the asset's dust floor does not block it.
</Warning>

A [`wallet.rotated`](/webhooks#walletrotated) webhook fires when rotation completes, carrying both the old `walletId` and the `replacedByWalletId`.

## Receiving Crypto

Share the wallet's `address` and incoming deposits surface in the wallet's `balances[]`. The end-to-end flow, including how deposits are detected and credited, is covered in the [Receive Crypto](/guides/receive-crypto-lifecycle) guide.

## Related

<CardGroup cols={2}>
  <Card title="Non-Custodial Wallets" href="/concepts/non-custodial-wallets">
    The two-signature model, the customer verify page, and outbound co-signing.
  </Card>

  <Card title="Receive Crypto" href="/guides/receive-crypto-lifecycle">
    How deposits arrive and credit a wallet's balances.
  </Card>

  <Card title="Add a Crypto Wallet" href="/guides/add-crypto-wallet">
    Step-by-step: enable the feature, claim non-custodial control, enroll the
    roster, and activate.
  </Card>

  <Card title="Signing modes" href="/concepts/signing-modes">
    Who approves payouts (people with passkeys, your servers, or both) and how
    to choose.
  </Card>
</CardGroup>
