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 custodial vs non-custodial setup for this flow, 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, not set via API, 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. It is configured by Conduit out of band: an org-wide default with an optional per-customer override (the customer-level setting wins). There is no API to set it — arrange it with your Conduit representative. Programmatic unattended additionally requires an approval review by Conduit. 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"). 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 every supported chain in one claim (recommended). 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 entirelyclaim.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; solana and 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