Skip to main content

Overview

An organization is resolved from the API key. Every org method on TrustodianSDK reads the caller’s active membership out of the key, and there is no orgId parameter anywhere in the org surface. Organization support splits into four areas:

Organization Wallets

Create, list, rename, and delete org wallets; portfolios, UTXOs, history, corporate address book

Organization Vaults

Org multisig vault lifecycle per chain, and orders against org vaults

Governance & Policies

Security policies, daily spending limits, limit groups, audit logs

Key Management

Key shares, shareStore, and the wallet-password session

Roles and statuses

Every org call checks, in order, that the API key is valid, that it resolves to an active member of the organization, that the member’s role is allowed for that operation, and that the key carries the required permission. A role or permission mismatch is a 403 and surfaces as PermissionDeniedError.
A couple of operations require a passkey step-up for browser sessions, but for API-key callers the check defers to the key’s own permissions. No step-up material is needed from the SDK.

Reading the org context

getOrgMe() is allowed for both Master and Employee and needs no API-key permission at all, which makes it the cheapest probe to run on SDK init before any other org call. Its organizationId is the value the organization-scoped reads take, such as listAllowances({ organizationId }) and getStrictAuthStatus({ organizationId }).
getOrgMe() resolves with organizationId, organizationName, orgMemberRole and memberId. An older backend leaves organizationId out. orgMemberStatus is typed as required but is not populated, so reading it yields undefined. Read a member’s status from getOrgTeam() instead; a caller whose membership is no longer active is rejected with a 403 anyway.

Team directory

userId, userName, kycStatus and joinedAt are null until the invitation is accepted. member.id is the memberId every other team method takes, not userId.

Invitations

The server enforces the 10-user organization limit and the global one-organization-per-email rule; both come back as ApiError with status 409.
inviteToRegister({ email }) is not part of the published SDK API surface. Use inviteOrgMember for organization membership, and do not build a flow on inviteToRegister without confirming it against your deployment first.

Removing a member

Removal runs one transaction that:
  1. deletes every wallet assignment for the member,
  2. deletes the member’s server shares for those wallets (never the Master’s own),
  3. voids the member’s signatures on pending org orders and recomputes the affected orders, writing one Org Signature Voided audit row per voided signature,
  4. writes the Org Member Removed audit row,
then, post-commit, deletes the member’s external MPC recovery shares and revokes their organization session.

Wallet assignment

walletId may point at a wallet or a vault, and the backend validates that the asset belongs to the organization. A duplicate assignment returns 409.
unassignOrgWallet is idempotent. Unassigning a wallet the member does not have returns 204, not 404. A 404 here means the member is not in this organization.
Revocation does more than delete a permission. The backend also removes the employee’s key material for that wallet and retracts the pending-order signatures the member cast with it, recomputing every affected order. The member’s session is not revoked, so they keep their other org access.

Employee wallet provisioning

Assignment alone grants no cryptographic access. Signing capability moves through a three-step handshake in which neither party ever learns the other’s wallet password, and no readable private key is ever transmitted.
1

Assign (Master)

Creates the permission record. cryptoAccessStatus on the member’s wallet listing is 'none'.
2

Provision (Master)

The call rebuilds the wallet key in the Master’s process and hands it over encrypted to a one-time tempPassword bound to that member. Nothing readable reaches the server. Hand tempPassword to the employee over a channel you control. It is returned once and is not recoverable.
Pass derivationSource and index straight through from listOrgMemberWallets() / listOrgWallets(). A wallet with derivationSource: 'seed' is shareless: its key is re-derived from the Master’s recovery phrase at index. Omitting these fields sends provisioning down the per-wallet share path, which hard-fails on a seed wallet. Pass derivationSeedId from the same listing too: it names the phrase the wallet derives from. Without it the SDK finds the phrase by matching the address.
3

Accept (Employee)

The employee decrypts what the Master handed over, checks that the key really produces address, and then immediately re-encrypts it under their own wallet password, so the one-time password stops being useful the moment accept succeeds. The employee’s device share is written to their shareStore. createEncTokenIfMissing defaults to true, so an employee with no wallet password yet has one established during accept.
If the accept succeeds server-side but persisting the device share fails, the SDK throws "Access accepted on server but failed to save device share locally. Try accepting again." The rotation already landed, so re-running accept with the same tempPassword is the recovery path.
4

Revoke (Master)

Server-side share cleanup plus signature retraction, as described above.
For splitting the provisioning handshake across processes, see Lower-level building blocks on Organization Wallets.
This grants the wallet, not a seat in a Bitcoin or Cardano vault. Those vaults put each owner on a separate cosigner key that wallet provisioning does not cover, so an employee who needs to sign there takes a second grant. See Vault keys for Bitcoin and Cardano.

Crypto access status

The member wallet listing carries the state machine:
'none' means assigned only, 'pending' means provisioned and awaiting accept, 'accepted' means the employee holds usable shares.

API-key permissions

Each operation requires any one of the listed permissions.
Accepting a wallet handover is Employee-only, and a Master key calling it gets 403. Provisioning is two-sided: it cannot be driven end to end from a single key.
Last modified on October 27, 2026