Skip to main content

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.
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), which by default provisions a wallet on the default set of supported chains in one step, or on the specific chains you request.
Only receiving crypto to fund a conversion? You may not need customer wallets at all: a deposit-funded order returns a funding address per order, with no claim, no signer roster, and no co-signature.

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.

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 to choose between them, and Programmatic payout signing for the machine flow.

The Wallet Resource

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

Historical balances

GET /v2/customers/{customerId}/wallets/{walletId}/balances returns the same elements on their own. Add ?date=2026-08-31 to read what the wallet held at the end of that UTC day, meaning the instant 2026-09-01T00:00:00Z. Omit date and you get what the wallet holds now, the same figures as balances[] on the wallet itself. date comes back only when you sent it. The day must be over: today, a future day, or anything that is not YYYY-MM-DD returns 400 VALIDATION_ERROR. A day before the wallet existed is not an error — every bucket reads zero.

Status lifecycle

A wallet enters pending with a null address while it is being provisioned. Rather than polling, subscribe to crypto_wallet.completed to learn when provisioning has finished and the address is populated.

Stellar setup

A Stellar address can receive USDC only after a one-time on-chain setup: the account must exist and hold a USDC trustline. Conduit pays for the setup and holds no funds of the customer to do it. For a custodial wallet the setup runs when you create the wallet. For a non-custodial wallet the customer’s signers must approve it, because the setup is signed with the wallet’s own key:
  1. When the wallet is created, it reads pending with its address set, and wallet.awaiting_setup_signature fires.
  2. The signers approve the setup at the same signing threshold as a payout: a human signer on the verificationUrl, a machine signer through POST /v2/signing-requests/{id}/approve (kind: "wallet_setup").
  3. Conduit submits the setup. The wallet reads active and wallet.created fires. Do not send funds to the address before this.
If nobody approves before expiresAt, or a signer declines, the wallet stays pending. Contact support to open a new setup request.

Supported Chains

Wallets can be created on the following chains. The chain value is always lowercase. The EVM chains (ethereum, base, polygon, tempo, and arbitrum) 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, tron and stellar each have their own distinct address.
solana is created only when you request it explicitly in chains; it is not part of the default chain set. That is a provisioning default, not a capability limit — once a solana wallet exists it receives, sends and funds orders. arbitrum is not in the default chain set either. While Arbitrum is open for deposits, a new wallet on another EVM chain also gets an arbitrum wallet at the same address; an older wallet does not. Before you send USDC on Arbitrum, check that the customer has an active arbitrum wallet, or create one with "chain": "arbitrum".What Conduit supports is an asset and a chain together, not a chain on its own, so a chain listed above does not mean every asset moves on it. Create an order lists every fundable pair on its sourceAsset field. An order naming any other pair is refused with UNSUPPORTED_ASSET; a payout is refused as a validation error on assetAmount.chain, and that message names every pair Conduit accepts.

Tempo

A tempo wallet can be custodial or non-custodial, and it has the same address as the customer’s wallets on the other EVM chains. A non-custodial wallet account holds a tempo wallet only after Conduit enables Tempo for that account. Until then, POST /v2/customers/{customerId}/wallets with "chain": "tempo" returns 422 NON_CUSTODIAL_CHAIN_NOT_SUPPORTED. On a non-custodial Tempo wallet, the signers can approve token transfers and the return of a held payout, and nothing else (see Payouts held by the payee’s receive policy). To create a custodial one, your organization must be enabled for custodial wallets, and the customer needs the crypto_wallet feature approved, as for a claim (Step 1 of Add a Crypto Wallet); without it the call returns 403 FEATURE_NOT_ENABLED. Then call POST /v2/customers/{customerId}/wallets with {"chain": "tempo"} and an idempotency-key header, with no claim: the first call provisions the customer’s custodial wallet account. A customer with custodial wallets on other EVM chains already holds that account, and its Tempo wallet reuses their address. A Tempo wallet holds two assets, both with 6 decimals: OUSD, native at 0x20c0000000000000000000006a37da5c996874be, and USDC, the bridged USDC.e at 0x20c000000000000000000000b9537d11c60e8b50. On the sandbox, a Tempo wallet holds OUSD only (see Limitations). Send only the tokens your environment supports to a Tempo wallet: Conduit does not credit any other token on Tempo. Tempo has no native coin. Conduit pays the network fee for every transfer it sends from a Tempo wallet, so the wallet never needs a fee balance.
The customer’s first wallet sets up its custodial wallet account. When that first wallet is created, wait about 30 minutes before you send the first deposit to its Tempo wallet. A deposit sent sooner is not credited automatically. Contact support if one does not appear.

Creating Wallets

Non-custodial 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. A custodial Tempo wallet is created without a claim (see Tempo). 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.
1

Claim non-custodial control (once per customer)

Opt the customer into the non-custodial multi-signer model:
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, less any chain that does not settle in the environment (on sandbox, tron), 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 and stellar are not in the default set: name them in chains to provision them. tempo cannot be claimed, because a Tempo wallet is custodial (see Tempo). A Stellar wallet reads pending until the signers approve its one-time on-chain setup (see Stellar setup).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.
2

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:
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 webhook carries the same admin link. On approval, wallet.created 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 for the multi-signer model and the WALLET_NO_PROVIDER_ACCOUNT error page for the resolution path.

Reading Wallets

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.
Returns 200 with the replacement wallet. The idempotency-key header is required.
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. Rotation is also blocked while Conduit is returning a coin it does not support, cannot yet prove whether an earlier return sent it, or can still return it: Conduit knows the sending address and has not refused the return after screening.
A wallet.rotated 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 guide. A coin that Conduit does not support for deposits on that chain is not credited and does not appear in balances[]. Conduit can send it back, in full, to the address it came from, and Conduit pays the network fee. That return appears as a deposit_return transaction with no returnOf. On a non-custodial wallet, the wallet’s signers approve the return (Non-Custodial Wallets). Do not send a coin from an exchange account that needs a memo or a destination tag: the return goes to the sending address, not to your exchange account.

Non-Custodial Wallets

The two-signature model, the customer verify page, and outbound co-signing.

Receive Crypto

How deposits arrive and credit a wallet’s balances.

Add a Crypto Wallet

Step-by-step: enable the feature, claim non-custodial control, enroll the roster, and activate.

Signing modes

Who approves payouts (people with passkeys, your servers, or both) and how to choose.