Skip to main content
This guide walks the full setup for a customer’s non-custodial crypto wallets, from enabling the feature to a set of active wallets ready to receive and send. For the concepts behind each step see Crypto Wallets, Non-Custodial Wallets, and Multi-signer wallets. To move funds afterward, see Receive Crypto and the Non-Custodial Payout Lifecycle. The customer must already be onboarded and active. You get customerId from the application.approved webhook; see Onboard a Customer. Calling these endpoints before the customer is active returns CUSTOMER_NOT_ONBOARDED.
Test this flow in sandbox. Drive it end-to-end with simulated money and deterministic controls: start with the sandbox quickstart, then the multi-signer recipes for roster and signing scenarios, and the cheat sheet for every magic value and simulate endpoint.

Flow

  1. Know the customer’s signing mode: it is arranged with Conduit, and it dictates the roster shapes Step 2 will accept.
  2. Request the crypto_wallet feature; wait for application.approved (applicationType: "crypto_wallet").
  3. Claim non-custodial control with a signer roster and threshold. The roster’s shape (passkey members, machine (api_key) members, or a mix) is set by the customer’s signing mode (Step 0).
  4. Passkey members enroll via their invitation link (delivered on wallet_signer.invited); machine members are active immediately, and an all-machine roster skips this step.
  5. Wait for crypto_wallet.completed: the wallets are now active.
No wallet address exists until the claim completes: the crypto_wallet feature only makes the customer eligible to claim.

Step 0: Know the customer’s signing mode

Every customer has a signing mode that dictates which roster shapes a claim accepts and how the wallets activate. Conduit sets it for each customer; to confirm or change it, ask your Conduit representative. Programmatic unattended additionally requires an approval review by Conduit. For choosing between the modes, and what each one commits you to, see Signing modes. These rules are enforced at claim time and again on every later signer add. A roster that breaks the mode’s shape (an api_key member under passkey-required, or an api_key admin under programmatic) returns 422 SIGNING_MODE_ROSTER_INVALID; a programmatic roster whose machine keys cannot cover the threshold (including zero machine keys) returns 422 PROGRAMMATIC_QUORUM_UNREACHABLE.

Step 1: Request the crypto-wallet feature

Discover any extra requirements, then submit the feature application. Idempotency-Key is required; without it the call returns IDEMPOTENCY_KEY_REQUIRED.
Track the feature application through application.approved / application.rejected (applicationType: "crypto_wallet"). A rejection carries resubmittable: when it is true, correct the data and request the feature again; when it is false, the decision is final. Customers in restricted jurisdictions are rejected up front with CRYPTO_NOT_AVAILABLE_IN_JURISDICTION; calling the next step before the feature is approved returns CRYPTO_FEATURE_NOT_APPROVED.

Step 2: Claim non-custodial control

One call mints the customer’s signing setup and provisions the initial wallets. The roster needs at least two admins; signingThreshold sets how many stamps each payout needs. chains is optional: omit it to provision a wallet on the default set of supported chains in one claim (recommended), or pass an explicit list to choose the chains (for example ["solana"], which is not in the default set). Pick the roster shape that matches the customer’s signing mode (Step 0). A machine (api_key) member carries a publicKey: a compressed P-256 public key you generate and hold, 33 bytes hex-encoded (66 hex chars, 02/03 prefix; optional 0x). Conduit only ever receives the public half; no field anywhere carries a private key. Missing key → 400 API_KEY_PUBLIC_KEY_REQUIRED; malformed → 400 API_KEY_PUBLIC_KEY_INVALID; two members sharing one → 422 API_KEY_PUBLIC_KEY_DUPLICATE.
The passkey-required shape: every member enrolls a passkey in Step 3.
Returns 202 Accepted with a claimId. The claimId is a correlation receipt, not a status handle. Don’t poll it; wait for the webhooks. See Multi-signer wallets for roster rules (admin vs signer, thresholds, root quorum) and Crypto Wallets for the shared EVM address and optional chains.

Step 3: Enroll the passkey members

This step applies to passkey members only. Machine (api_key) members seat active at claim: no invite webhook, no URL, no enrollment (passkeyCount stays 0). An all-machine roster skips this step entirely: claim.completed is your activation signal, with no signer action in between. For passkey members: Conduit never contacts signers directly. Each one gets a wallet_signer.invited webhook carrying their own verificationUrl and expiresAt; forward each URL to the intended signer. The signer opens it and registers their passkey on-device. A plain signer enrolls one passkey; an admin enrolls two (a backup device).
Each signer emits wallet_signer.enrolled on their qualifying stamp. See Non-Custodial Wallets for the hosted verify page. In sandbox, POST /v2/sandbox/wallet-signers/:signerId/mark-enrolled collapses a passkey member’s enrollment headlessly (machine members never need it).

Step 4: Wait for activation

Once every passkey member has enrolled, the wallets activate. Listen for claim.completed: it carries the claimId from Step 2 plus the activated walletIds, so you can close the loop on the claim without polling. A customer-level crypto_wallet.completed also fires, carrying customerId only (no wallet IDs). Either is your signal the customer can receive deposits and originate payouts.
Every wallet reads status: "active" with an address. The three EVM chains (ethereum, base, polygon) share one address; non-EVM chains like tron each have their own. GET /wallets hides wallets until they activate, so this list is empty between the claim and crypto_wallet.completed.

What’s next