Add a signer to a customer's wallets
Adds a new signer to the customer’s wallet infrastructure. An add to a live (already-active) roster typically needs one admin approval — see Multi-signer wallets. The synchronous response returns a verificationUrl whose audience is given by urlAudience: a passkey signer gets their own enrollment link (urlAudience: "signer", also delivered via the wallet_signer.invited webhook) — deliver it to the signer. For a passkey signer added to a live roster this "signer" link is the FIRST of two steps, not full self-service: once the signer enrolls their passkey the add parks a roster-add ceremony for a customer admin’s co-stamp, and that admin link arrives ONLY on the wallet_ceremony.awaiting_admin_approval webhook (carrying the ceremony’s ceremonyId), never in this response. An api_key signer that parks for admin approval instead returns the admin co-stamp link inline (urlAudience: "admin", also delivered via the wallet_ceremony.awaiting_admin_approval webhook) — deliver it to an admin; a synchronously-admitted api_key signer returns null/null. A parked api_key add whose activity is still resolving at the custody provider ALSO returns null/null, but with awaitingProvider: true set — that flag is what distinguishes “nothing to do, fully admitted” from “nothing to do yet, still resolving” until the admin link becomes available (poll reissue-admin-approval, which mints it once the provider settles). If the admin webhook is lost, recover the admin link with POST /wallet-signers/{signerId}/reissue-admin-approval. When an api_key add parks on a roster that carries an ACTIVE api_key admin, the response ADDS machineApproval (a token + approvalMaterial) alongside that admin link and webhook: stamp the material with an ACTIVE api_key admin key and POST { method: "api_key", credential } to /v2/verifications/{token}/complete (credential is a JSON string of { signedBody, stamp, organizationId }) to activate the signer without opening the link — the same stamp-and-approve step every machine governance ceremony uses. Conduit never emails anyone — your platform delivers the link. Route by urlAudience, never assume the URL is the signer’s.
The same signing-mode roster rules as the claim apply to later adds: api_key signers are only valid under a programmatic signing mode, and api_key admins only under programmatic unattended — otherwise 422 SIGNING_MODE_ROSTER_INVALID. An api_key signer must include publicKey (compressed P-256: 33 bytes / 66 hex chars, 02/03 prefix); a key already on an active signer returns 409 API_KEY_PUBLIC_KEY_IN_USE. See Programmatic payout signing.
Authorizations
Headers
Caller-generated unique key that lets the server safely replay this request. The cached response is returned for 5 minutes on any retry with the same key from the same API principal. Required on every state-changing money-moving or resource-creating POST.
1 - 128^[A-Za-z0-9_.:-]{1,128}$Path Parameters
Body
Email address of the signer to add
^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$"signer@example.com"
Display name of the signer
"Jane Doe"
admin = root-quorum member + signing-quorum member; signer = signing-quorum only
admin, signer How this signer will stamp. api_key requires publicKey.
passkey, api_key Machine-signer P-256 compressed public key (33 bytes, 02/03 prefix + 64 hex chars, optional 0x prefix). Required when credentialType=api_key.
"02a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
Response
Unique wallet signer identifier
^wsg_[0-9A-Za-z]{22}$Signer's email address
"signer@example.com"
Signer's display name
"Jane Doe"
admin = root-quorum member + signing-quorum member; signer = signing-quorum only
admin, signer How this signer stamps requests
passkey, api_key Number of passkeys enrolled for this signer
0 <= x <= 90071992547409910
Current signer status
pending_activation, activating, active, pending_removal, removed, error Timestamp when the signer was created
"2026-01-15T09:30:00.000Z"
Timestamp when the signer was last updated
"2026-01-15T09:30:00.000Z"
Synchronous URL for this add, or null when none applies. Read urlAudience to know who it is for before delivering it — a passkey add returns the signer's enrollment link, an api_key add pending approval returns a customer admin's co-stamp link. On a live roster a "signer" link is the FIRST of two links, not full self-service: after the signer enrolls their passkey the add parks a roster-add ceremony for an admin co-stamp, whose link arrives only on the wallet_ceremony.awaiting_admin_approval webhook (carrying the ceremony's ceremonyId).
"https://app.conduit.financial/verify/mtQzSU2hdy0DuZ6za2IHIoVDgXzshrFtN38kMULRDFw"
Audience of verificationUrl: signer = the new signer's credential-enrollment link (deliver to the signer) — on a live roster this is only the first of two links, the second being an admin co-stamp delivered on the wallet_ceremony.awaiting_admin_approval webhook; admin = a customer admin's approval link (deliver to an admin; also delivered via the wallet_ceremony.awaiting_admin_approval webhook); null when there is no URL.
signer, admin True only while the parked activity is still resolving at the custody provider — verificationUrl/urlAudience are null here too, but unlike a synchronously-admitted add there is a ceremonyId to track and nothing yet for any human to approve. Absent on every other outcome.
true Additional material for a machine (api_key) add that parks for approval on a roster carrying an ACTIVE api_key admin. It is handed back ALONGSIDE verificationUrl (urlAudience: "admin") and the wallet_ceremony.awaiting_admin_approval webhook, which are always present — approve either way. Stamp approvalMaterial with an ACTIVE api_key admin's P-256 key and POST { method: "api_key", credential } to /v2/verifications/{token}/complete (credential is a JSON string of { signedBody, stamp, organizationId }) to activate the signer — the same stamp-and-approve step every machine governance ceremony uses. Absent when the roster has no api_key admin to stamp.