SDK — add a member to a business account
Owner or admin by default; an account whose governance sets
eligibleInitiatorRoles for addMember can widen that. Target user is
resolved by userId OR identifier + type (email/externalUserId
create-or-resolve; id direct lookup). Idempotent: an already-active member
returns the existing row with 200.
When the account governs addMember the response is 202, not 201 — first
with the intent to sign, then with the proposal collecting approvals.
Neither is a failure.
Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Path Parameters
ID of the environment
36^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"95b11417-f18f-457f-8804-68e361f9164f"
ID of the business account
36^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"95b11417-f18f-457f-8804-68e361f9164f"
Body
SDK addMember body. Caller resolves the target user by either passing userId directly OR by identifier + type (any UserIdentifierTypeEnum value), mirroring the pregen flow. smsCountryCode is required when type is phoneNumber; socialProvider is required when type is socialUsername or socialAccountId.
A role that can be directly assigned to a member: a built-in (admin, viewer) or a customer-defined role the account has defined. owner is excluded — ownership moves only via transferOwnership.
Not an enum, because the set is per-account: a caller can grant any role from GET /roles. Whether the role exists is the enclave's to answer, not this schema's — the pattern only rejects names that could never be one. A role the account has not defined is refused with UNKNOWN_ROLE.
A customer-defined role grants exactly what it inherits, so cfo inherits admin is an admin with a distinguishable name.
^[a-z0-9](?:[a-z0-9_-]{0,30}[a-z0-9])?$"cfo"
email, id, externalUserId, phoneNumber, socialUsername, socialAccountId The 'turnkey' value is deprecated and will be removed in a future version.
emailOnly, magicLink, apple, bitbucket, coinbasesocial, discord, epicgames, facebook, farcaster, github, gitlab, google, instagram, linkedin, microsoft, twitch, twitter, blocto, banxa, coinbaseOnramp, cryptoDotCom, moonPay, dynamic, alchemy, zerodev, telegram, turnkey, coinbaseWaas, sms, spotify, tiktok, line, steam, shopify, zksync, kraken, blockaid, passkey, okta, sendgrid, resend, trmWalletScreening, chainalysisAddressScreening, gemini Whether the change applies the moment its quorum is met, or waits for an explicit execute. Read only on the first call, where it is stamped into the intent you sign; after that the signed intent is the authority. Defaults to true, because a change holding all its consent while waiting for a button press tends to be forgotten, and the deadline covers execution too.
The initiator's proposal, signed with their session key. Built by the API, not the client — sign these exact bytes verbatim, because re-serializing can change them and invalidate the signature.
autoExecute is inside the signed bytes, so an approver consents to it and nothing can flip it afterwards.
Hex-encoded ECDSA P-256 signature over the canonicalized payload, produced by the signer's session key — which never leaves their device, so consent cannot be given on their behalf. Exactly 128 hex characters. WebCrypto emits the raw r‖s form with both halves padded to 32 bytes, so unlike DER the length never varies. Constrained here so a malformed value is a 400 at the edge rather than an enclave round trip that returns INTENT_SIGNATURE_INVALID, and so an unbounded string cannot be persisted against a proposal.
^[0-9a-fA-F]{128}$Response
Target user was already a member; existing row returned
Admin-reach membership in a business account
36^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"95b11417-f18f-457f-8804-68e361f9164f"
36^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"95b11417-f18f-457f-8804-68e361f9164f"
36^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"95b11417-f18f-457f-8804-68e361f9164f"
The role a member actually holds: a built-in (owner, admin, viewer) or a customer-defined role the account has defined. Wider than AssignableBusinessAccountRoleName because a held role can be owner, which cannot be assigned directly.
^[a-z0-9](?:[a-z0-9_-]{0,30}[a-z0-9])?$"cfo"
Member's verified email; null when they have none