Skip to main content
Multi-signer is the canonical non-custodial wallet model. The customer holds their own signing material across a team: they enroll a roster of named signers (each with their own passkey), pick a signing threshold, and every payout pauses on a quorum gate until enough signers stamp it. New customers reach a usable wallet exclusively through this path. This page is the single mental model. For the deposit/payout walkthroughs that exercise these endpoints, see Withdrawals and the Multi-signer wallets recipe (with roster ceremonies).

When to use which custody model

New customers always provision multi-signer non-custodial wallets via the claim endpoint. The custodial column below applies only to customers that were provisioned through the legacy CRYPTO_WALLET application before the non-custodial gate; converting one of those to non-custodial uses the WALLET_CUSTODY_CONVERSION application.
Custodial (legacy)Non-custodial (single-signer, converted)Multi-signer non-custodial
Who holds signing materialConduitCustomer (one passkey on a single end-user)Customer team (one passkey per roster member)
Provisioned viaLegacy CRYPTO_WALLET application onlyApproved WALLET_CUSTODY_CONVERSION applicationPOST /v2/customers/:id/wallets/claim-non-custodial
Payout signingAutoSingle passkey via verify pageM-of-N roster, each stamps independently
Use whenLegacy customers onlyConverting a legacy custodial customerDefault for every new customer
Multi-signer is the only model where you choose the M-of-N threshold. The first two collapse to a single signer or no signer.

Concepts

Roster. The ordered list of named signers attached to a customer’s non-custodial setup. Each signer has an email, a role (admin or signer), and exactly one passkey credential. Roster size and threshold are decoupled: a 5-member roster can require 2 stamps; a 2-member roster can require 2. Threshold. The integer M in M-of-N. Every payout collects stamps until M are recorded, then auto-broadcasts. The threshold is set at claim-non-custodial time and can be adjusted later via the signing-quorum endpoints. See Signing thresholds for the constraints. Admin vs signer. Both roles stamp payouts; only admins can change the roster (add, remove, promote, demote other members). The minimum-admin floor is enforced on every roster mutation so you can never lock yourself out. Root quorum. Conduit’s underlying signing infrastructure binds admin signers to a root quorum that protects the sub-organization itself. Membership flips happen through a ceremony: a coordinated multi-step update that re-seats the root members. You see this surface as: a promote/demote operation returns 202 Accepted and the change shows up after the ceremony completes. Ceremony. A short-lived background flow that re-seats the root quorum (on promote/demote) or rolls a roster (on add/remove). Ceremonies are sequential per customer; a second one queues if one is already in flight. Status is read via the internal ceremonies-list endpoint (GET /v2/internal/customers/:id/wallet-ceremonies). Ghost-vote scrubbing. If a signer is removed while a payout is sitting at the quorum gate, that signer’s already-cast stamps are scrubbed from every in-flight payout for the customer. Affected payouts re-fire transaction.signature_collected with the new count; payouts that can no longer reach quorum on the new roster terminate with failureCode: "ROSTER_CHANGED" so clients can re-submit against the current roster.

Lifecycle endpoints

EndpointPurpose
POST /v2/customers/:customerId/wallets/claim-non-custodialProvision the customer’s multi-signer setup. Required body: roster, signingThreshold. Returns 202 with a claimId; per-signer enrollment URLs fan out as wallet_signer.invited webhooks.
POST /v2/customers/:customerId/wallet-signersAdd a signer to an existing roster. Returns the new signer row in PENDING_ACTIVATION. A wallet_signer.invited webhook delivers the verification URL.
DELETE /v2/customers/:customerId/wallet-signers/:signerIdRemove a signer. Admins must be demoted first (returns 409 SIGNER_IS_ROOT_MEMBER otherwise).
POST /v2/customers/:customerId/wallet-signers/:signerId/promotePromote a signer to admin. Triggers a root-quorum ceremony.
POST /v2/customers/:customerId/wallet-signers/:signerId/demoteDemote an admin back to signer. Triggers a root-quorum ceremony.
For the quorum-threshold endpoints (per-customer + per-wallet overrides) see Signing thresholds.

Sandbox simulators

EndpointPurpose
POST /v2/sandbox/wallet-signers/:signerId/mark-enrolledMarks a PENDING_ACTIVATION signer as enrolled. Fires wallet_signer.enrolled; once every roster member is enrolled, the wallet flips to ACTIVE and crypto_wallet.completed fires.
POST /v2/sandbox/payouts/:id/simulate-stampRecords one signer’s stamp against a multi-signer payout. Body carries walletSignerId and selection (APPROVED or REJECTED). Returns the post-stamp votesCollected / votesRequired.
POST /v2/sandbox/customers/:customerId/simulate-reset-claimWipes the customer’s non-custodial setup (every signer, every wallet, the crypto-wallet feature flag) so a subsequent claim-non-custodial starts fresh. Refuses with 409 CLAIM_RESET_BLOCKED if open transactions, deposits, or pending signature approvals still reference the customer; terminalize them via simulate/terminal first.

Webhooks

TopicFires when
wallet_signer.invitedOne per roster member at claim time or POST /wallet-signers. Payload carries verificationUrl and expiresAt.
wallet_signer.addedCo-emitted with wallet_signer.invited; carries the steady-state membership shape (role, credentialType, no URL).
wallet_signer.enrolledA signer completed their verification and is now ACTIVE.
wallet_signer.removedA signer was removed from the roster (direct removal only; demote does not emit removed).
wallet_signer.promotedA signer was promoted to admin after the root-quorum ceremony cleared.
wallet_signer.demotedAn admin was demoted to signer after the root-quorum ceremony cleared.
transaction.awaiting_user_signaturePayout parked at the quorum gate. Payload carries verificationUrl and expiresAt (the same URL also lives on per-signer wallet_signer.invited payloads for the enrollment phase).
transaction.signature_collectedOne stamp recorded. Payload carries votesCollected and votesRequired. May re-fire if a roster change scrubs a stamp.
transaction.quorum_metQuorum reached; payout proceeds to broadcast.
transaction.failed with failureCode: "ROSTER_CHANGED"A roster change scrubbed enough stamps that the payout can no longer reach quorum on the new roster.

Error codes on the multi-signer surface

CodeSurfaceMeaning
CUSTOMER_ALREADY_CUSTODIALclaim-non-custodialThe customer already provisioned a custodial wallet via the legacy feature endpoint.
CUSTOMER_ALREADY_NON_CUSTODIALclaim-non-custodialThe customer already has a non-custodial wallet from a previous claim.
CUSTOMER_KYB_INCOMPLETEclaim-non-custodialThe customer has not yet been KYB-approved.
ROSTER_BELOW_MIN_ADMINSclaim-non-custodial, remove, demoteThe proposed roster has fewer than the required minimum of admins.
THRESHOLD_EXCEEDS_ROSTERclaim-non-custodial, add, removesigningThreshold cannot exceed the number of ACTIVE signers.
SIGNER_EMAIL_DUPLICATEclaim-non-custodial, addTwo roster members share an email.
SIGNER_NOT_FOUNDremove, promote, demote, simulate-stampThe signer id does not exist for the customer.
SIGNER_NOT_ACTIVEremoveSigner is still in PENDING_ACTIVATION (never enrolled).
SIGNER_IS_ROOT_MEMBERremoveRemoving an admin directly would skip the root-quorum ceremony; demote first.
SIGNER_ALREADY_ADMINpromoteTarget signer is already admin.
SIGNER_NOT_ADMINdemoteTarget signer is not currently admin.
CEREMONY_IN_FLIGHTadd, remove, promote, demoteA previous ceremony is still running for this customer; retry after a short backoff.
WOULD_BREAK_MIN_ADMINSremove, demoteThe change would drop the admin count below the minimum floor.
QUORUM_THRESHOLD_EXCEEDS_SIGNERSquorum endpointsThe requested threshold is greater than the active signer count.
QUORUM_WALLET_OVERRIDE_CANNOT_RAISEper-wallet quorum overrideA per-wallet override cannot exceed the customer-default threshold.
CLAIM_RESET_BLOCKEDsimulate-reset-claimOpen transactions, deposits, or pending signature approvals still reference the customer; terminalize them first.
PROVIDER_ACCOUNT_NOT_FOUNDsimulate-reset-claimThe customer has no non-custodial setup to reset.