Setup and access
What do I install, and what do I import?
What do I install, and what do I import?
vaults-multichain-sdk. The client class is TrustodianSDK.apiKey, network and shareStore are all mandatory, and the constructor throws if any is
missing. Everything else has a default. See Quickstart.How do API keys and permissions work?
How do API keys and permissions work?
x-api-key header; the server resolves it to a user and,
if the key belongs to an organization, to a role and member id. You never pass a user id or an
org id yourself.A key holds a set of permissions drawn from view_balances, view_transaction_history,
wallet_management, vault_management, transaction_management, team_management,
policy_management, address_book_management and audit_log_access. Each operation declares which
ones they accept, so a key missing the right permission gets a 403 (surfaced as
PermissionDeniedError) even though the key itself is valid. Keys can also carry an IP
whitelist. See Authentication.Does the SDK run in a browser?
Does the SDK run in a browser?
- Using it means holding an API key and handling users’ wallet passwords. Both belong on your backend and nowhere else.
- It is built for Node and depends on APIs a browser bundle does not supply.
Can the SDK connect a Ledger or Trezor?
Can the SDK connect a Ledger or Trezor?
Does a hardware wallet need the wallet password?
Does a hardware wallet need the wallet password?
shareStore for it. The
exceptions are activating a Solana Vault, which also takes userRef and walletPassword because
the Vault activation key is under the wallet password, and executing or activating a Vault under
strict authentication.Can I set up the hardware root through the SDK?
Can I set up the hardware root through the SDK?
What is userRef?
What is userRef?
await sdk.getSdkUserId().
It is the key under which device shares are filed
in your shareStore, and the handle every session and crypto call takes. Use the same value
consistently; a mismatched userRef looks to the SDK like a different user with no shares.Can I call the REST API directly instead?
Can I call the REST API directly instead?
Secrets: recovery phrase vs wallet password
What is the wallet password?
What is the wallet password?
unlockWalletSession(userRef, password). The session lasts
keyTtlMs, 5 minutes by default, and can be dropped early with lockWalletSession(userRef).
Rotate the password with changeWalletPassword(userRef, { currentPassword, newPassword }),
which re-encrypts every share. See Key management.What is the account recovery phrase?
What is the account recovery phrase?
createWallet derives wallets from that existing seed rather than
generating standalone keys. The phrase is shown to its owner once, at establish time.What your integration uses: getRecoveryPhraseStatus to read the state. Re-displaying an
already-established phrase is not available over the API-key/SDK transport (as of SDK v3.0.0,
revealRecoveryPhrase throws PermissionDeniedError); that is a first-party Trustodian web app
operation only. See Recovery phrase.Why does every crypto call still want the password if the session is unlocked?
Why does every crypto call still want the password if the session is unlocked?
A user forgot the wallet password. Can the recovery phrase rescue them?
A user forgot the wallet password. Can the recovery phrase rescue them?
changeWalletPassword requires currentPassword. Reading the phrase back would not help
either, because it is gated behind the wallet password rather than the other way round — and
as of SDK v3.0.0 it is not reachable from your integration at all, only from the Trustodian web
app. Every stored share is encrypted under keys derived from that password, and Trustodian
cannot derive them for you.What you can actually do:getWalletPasswordHint()returns the hint the user set when they chose the password. Prompt for a hint at setup so this is worth something.getEncTokenStatus()tells you whether a password was ever configured for the account.
Can Trustodian spend my users' funds?
Can Trustodian spend my users' funds?
shareStore; the other two are stored server-side but encrypted under keys derived from the
user’s wallet password, which the server never sees. Holding the ciphertext without the password
rebuilds nothing.Shares, devices and sessions
What does an ADDRESS_MISMATCH error mean?
What does an ADDRESS_MISMATCH error mean?
code: 'ADDRESS_MISMATCH', not the exported KeyMismatchError class, whose
KEY_MISMATCH code is reserved and not thrown in practice.The fix is to rebuild the device share, and the SDK will do it for you: pass walletPassword to
the crypto call so it can recover in place, or call ensureWalletReadyForCrypto /
recoverWallet first. See Errors.Networks, chains and fees
How do I switch between testnet and mainnet?
How do I switch between testnet and mainnet?
network: 'mainnet' | 'testnet' at construction. It selects two things at once: the
default apiBaseUrl and the whole default RPC set. Chain identifiers differ too: EChainType
stays the same but the chain id changes ("1" vs "11155111", sol_mainnet vs sol_devnet,
tron_mainnet vs tron_shasta, ada_mainnet vs ada_preprod, xrp_mainnet vs
xrp_testnet). Bitcoin is the one case where the chain type differs: BTC on mainnet,
BTC_T3 on testnet.Build one SDK instance per network rather than trying to flip a live one.Can I use my own RPC endpoints?
Can I use my own RPC endpoints?
rpcOverrides is a flat map keyed by chain id, merged over the defaults for the selected
network. EVM chains are keyed by their
numeric id as a string; the other chains use their named id. There is no nesting and no evm
sub-object:Which chains are supported, and with which EVM chain IDs?
Which chains are supported, and with which EVM chain IDs?
EChainType has seven members: EVM, SOL, TRON, BTC, BTC_T3, ADA, XRP. On EVM the
SDK ships names and RPC defaults for Ethereum, Polygon, Base, Arbitrum One, Optimism, BNB Smart
Chain and Avalanche C-Chain, on both networks. The chain ids are listed in
Supported chains.Passing an EVM chain id with no configured RPC throws
No RPC endpoint configured for EVM chain ID <id>. Add it through rpcOverrides.How are fees paid, per chain?
How are fees paid, per chain?
activateMultisig and executeOrder take feeQuote (and gasLimit / gasPrice
on EVM). See Multisig fees.To put a number in front of a user, use estimateFee, a chain RPC, or preflightOrder.Transactions, orders and scope
Why does an order need both signing and executing?
Why does an order need both signing and executing?
signOrder rebuilds one owner’s key and adds that owner’s approval to a
pending order; it broadcasts nothing. Once enough owners have signed to meet the threshold,
executeOrder assembles the collected signatures into a transaction and broadcasts it,
paying the network fee from the executor’s wallet.That is also why they are two separate calls, and why a
single-owner flow still makes both: one signature is simply enough to reach the threshold.
rejectOrder is the third outcome. See Orders.When do I pass walletScope: 'organization'?
When do I pass walletScope: 'organization'?
walletScope is 'personal' | 'organization' and defaults to personal; it routes
the call to the organization-owned resource and tells the share layer which partition to read.The scope of the listing method and the scope you pass to the action must agree: a wallet
returned by listOrgWallets / listMyOrgWallets must be acted on with
walletScope: 'organization'. Getting this wrong reads the wrong share set and fails, usually
as a 403 or a key mismatch. See Organization and
Organization wallets.How do I test a transfer safely before sending it?
How do I test a transfer safely before sending it?
validateAddressForChain/assertValidAddressForChaincatch a malformed recipient locally, with no network call. See Transactions.preflightSend/preflightOrdercheck the whole thing before any key is touched. See Preflight.- Run against
network: 'testnet'with a separate API key, on Sepolia / devnet / Shasta /BTC_T3/ preprod / XRP testnet. - Turn on the address-book whitelist so an unlisted destination is refused server-side. See Address book.
Operations
Are there rate limits, and how do I handle them?
Are there rate limits, and how do I handle them?
429 and the SDK raises RateLimitError with retryAfter
parsed from the Retry-After header when the server sends one. Back off on that value; do not
spin.What are the timeouts?
What are the timeouts?
timeout in the SDK
config. An overrun surfaces as TimeoutError carrying timeoutMs and the operation, which is
distinct from NetworkError for a failed connection, so you can retry the two differently.Chain operations legitimately take longer than an API read; if you raise timeout, raise it for
the instance that does signing and execution, and keep read paths tight.How do I tell an SDK error from any other exception?
How do I tell an SDK error from any other exception?
TrustodianBaseError and carries a stable code. isTrustodianError
is the type guard; hasErrorCode(error, code) narrows to one specific code. The full catalogue
is in Errors.