> ## Documentation Index
> Fetch the complete documentation index at: https://sdk-guide.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Organization Vaults

> Org-owned multisig vaults, their per-chain activation lifecycle, and org orders

## Overview

An organization vault is an ordinary Trustodian multisig whose owners are org wallets and which is tagged with the caller's `organizationId`. It behaves exactly like a personal vault, with the same shapes, signature threshold and order lifecycle, but it is owned and governed by the organization rather than by one user.

The SDK makes that switch with one field:

```typescript theme={null}
const vault = await sdk.createMultisig({
  chain: EChainType.EVM,
  owners: [orgWalletA, orgWalletB, orgWalletC],
  threshold: 2,
  chainIds: ['1'],
  walletScope: 'organization',
  userRef,
});
```

> [!NOTE]
>
> Organization vaults take no hardware wallet owners. Adding one is refused with
> `VAULT_OWNER_HARDWARE_ORG_UNAVAILABLE`.

Read [Multisig Wallets](https://sdk-guide.mintlify.site/sdk-guide/multisig.md) for the mechanics of vault creation and activation, and [Orders](https://sdk-guide.mintlify.site/sdk-guide/orders.md) for the proposal lifecycle. This page covers only what is different when `walletScope` is `'organization'`.

## Visibility: every vault versus mine

| Method | Who | What it returns |
| - | - | - |
| `listOrgVaults` | Master only | Every active vault tagged to the organization, regardless of who owns the wallets |
| `listMyOrgVaults` | Master or Employee | Only active vaults whose owner set includes a wallet assigned to the calling member |

An Employee calling `listOrgVaults()` gets `403`, surfaced as `PermissionDeniedError`. A Master calling `listMyOrgVaults()` gets the participation-filtered subset rather than the full inventory, so the two are not interchangeable even for a Master. Deactivated vaults and vaults from other organizations are filtered out of both.

```typescript theme={null}
interface OrgVaultItem {
  id: string;
  address: string | null;   // null until deployed
  name: string | null;      // null until deployed
  chainType: ChainType;
  chainId: string;          // "1", "137", "tron_mainnet", ...
  threshold: string;        // numeric string
  isDeployed: boolean;
  owners: string[];         // bare owner wallet addresses
}

interface OrgVaultsResponse {
  vaults: OrgVaultItem[];
  total?: number;  // paginated requests only
  page?: number;   // paginated requests only
  limit?: number;  // paginated requests only
}

interface OrgMyVaultsResponse {
  vaults: OrgVaultItem[];
}
```

```typescript theme={null}
const all = await sdk.listOrgVaults();                       // full list, no metadata
const page = await sdk.listOrgVaults({                       // opt-in pagination
  page: 1,
  limit: 20,
  chainTypes: [EChainType.EVM],
});
const mine = await sdk.listMyOrgVaults();
```

> [!WARNING]
>
> `OrgVaultItem` is a flat shape: `owners` are bare address strings, and there is no `factoryAddress`, `params.deployData` or `tokens`. It is not enough to activate a vault, so use `listOrgVaultsByChain` or `getOrgVaultById` when you need the activation payload.

## Full-detail reads

```typescript theme={null}
const detailed = await sdk.listOrgVaultsByChain({ chainType: EChainType.EVM });
// MultisigsWithTokensResponse

// One vault by on-chain address
const result = await sdk.getOrgVaultById({
  address: vaultAddress,
  chainType: EChainType.EVM,
});
const item = result.multisig.data[0];
// item.factoryAddress, item.params.deployData, item.owners[].walletAddress
```

> [!WARNING]
>
> `getOrgVaultById` returns `MultisigWithTokensResponse`. Read the vault through `vault.multisig.data[0]`, so `vault.multisig.data[0].address` and `vault.multisig.data[0].owners.map((o) => o.walletAddress)`. Older code that read `vault.address` / `vault.owners` needs updating.

> [!NOTE]
>
> `listOrgVaultsByChain` is callable by Master and Employee alike, and needs any of `ViewBalances`, `VaultManagement` or `TransactionManagement`.

### History

```typescript theme={null}
const history = await sdk.getOrgVaultHistory({
  address: vaultAddress,
  chainType: EChainType.EVM,
  page: 1,
  limit: 20,
  hideDust: true,
});
// TxHistoryCombinedResponse: orders plus transfers
```

Master or Employee; for Employees it is assignment-gated the same way the vault read is.

## Creation and activation lifecycle

Every org vault call checks the API key, active organization membership, the member's role and the key's permissions. On top of that:

* Creation requires a verified identity and 2FA, is Master only, and needs `VaultManagement`.
* Activation requires 2FA and, where the organization enforces it, KYC. It allows Master or Employee, and needs `VaultManagement`.

> [!WARNING]
>
> A vault that does not exist, belongs to another organization, or is deactivated all return the same
> `404 Vault not found`, and the difference is not disclosed. Treat that `404` as terminal;
> do not retry through the personal vault path.

```mermaid theme={null}
flowchart LR
    C["createMultisig<br/>Master · VaultManagement"] --> R{chain}
    R -->|EVM, SOL, TRON, XRP| A["activateMultisig"] --> D["vault live on chain"]
    R -->|BTC, ADA| B["spendable once funded"]
```

### What creation and activation need

| Chain | Create takes | Then |
| - | - | - |
| EVM | `owners`, `threshold`, `chains` | Activate to deploy the vault on chain |
| Tron | `owners`, `threshold`, `chains` | Activate; the vault must already hold at least 100 TRX |
| XRP | `owners`, `threshold`, `chains` | Activate; the vault must be funded above its reserve |
| Solana | `owners`, `threshold`, `creatorAddress`, `chain` | Activate |
| Bitcoin | `owners`, `threshold`, `creatorAddress`, `chain` | Nothing. Spendable once funded |
| Cardano | `owners`, `threshold`, `creatorAddress`, `chain` | Nothing. Spendable once funded |

Every chain also takes optional `ownerLabels`, one per owner (see
[Owners and threshold](https://sdk-guide.mintlify.site/sdk-guide/multisig.md#owners-and-threshold)).

Creation tags the vault to the caller's organization automatically. The vault is not on chain until
it is activated.

### Through the SDK

```typescript theme={null}
const userRef = await sdk.getSdkUserId();
await sdk.unlockWalletSession(userRef, password);

const vault = await sdk.createMultisig({
  chain: EChainType.EVM,
  owners: [orgWalletA, orgWalletB, orgWalletC],
  threshold: 2,
  chainIds: ['1'],
  walletScope: 'organization',
  userRef,
});

const feeQuote = await sdk.estimateFee({
  chain: EChainType.EVM,
  txType: TransactionEstimatingType.ActivateMultisig,
  payload: { from: creatorOrgWallet, multisig: vault },
  chainId: vault.chainId,
});

await sdk.activateMultisig({
  chain: EChainType.EVM,
  multisigId: vault.id,
  multisigAddress: vault.address!,
  walletAddress: creatorOrgWallet,
  multisig: vault,
  feeQuote,
  walletScope: 'organization',
  userRef,
});
```

`activateMultisig` picks the whole per-chain sequence for you: for Solana it fetches the blockhash from `sol/tx/deploy`, signs locally, and broadcasts to `sol/deploy`; for Tron and XRP it uploads the share and then broadcasts. `userRef` (or `runWithUserRef`) plus an unlocked session are required, because SOL, TRON and XRP need the activation-share encryption keys. `feeQuote` is a required field on `ActivateMultisigInput`.

Rename and delete stay on the personal vault methods (`updateMultisigName`, `deleteMultisig`); there is no org twin for either. Owner names work the same way: `setOwnerName` / `resetOwnerName` cover organization vaults too, and an employee who can see the vault may name its owners for themselves.

## Vault keys for Bitcoin and Cardano

On every other chain an employee signs a vault order with the org wallet they were already given through [employee wallet provisioning](https://sdk-guide.mintlify.site/sdk-guide/organization.md#employee-wallet-provisioning). Bitcoin and Cardano vaults do not work that way. Each owner sits in the vault on a separate cosigner key, and that key is not the wallet key: it is the vault account key, derived from the Master's recovery phrase at the seat's `accountIndex`. Wallet provisioning never touches it.

So an employee who holds an assigned org wallet and nothing else can open a BTC or ADA vault, see its balance and propose orders, but cannot sign one. They need a second grant, on the vault.

> [!WARNING]
>
> Provision the wallet and the vault seat both. Skipping the seat leaves an employee who looks fully set up until the first signature fails with `VaultKeyNotProvisionedError`.

Use `isCosignerPoolChain(chainType)` to decide whether a vault needs this at all. It covers `BTC`, `BTC_T3` and `ADA`; everything else is wallet provisioning only.

### The handshake

It mirrors the wallet one: the Master hands the key over encrypted to a one-time password, the employee re-encrypts it under their own password. A member can hold more than one seat in the same vault, and each seat is granted separately.

**1. Read the seats (Master)**

```typescript theme={null}
const { seats } = await sdk.getOrgMemberVaultSeat({ memberId, multisigId });
// seats: { cosignerKeyId, accountIndex, chainType, walletId, walletAddress,
//          seedId?, seedOwnerKind?, xpub?, paymentKeyHashHex? }[]
```

`seedId` and `seedOwnerKind` name the phrase the seat derives from. `xpub` (Bitcoin) and `paymentKeyHashHex` (Cardano) are the public key the seat must reproduce.

**2. Provision (Master)**

```typescript theme={null}
const masterUserRef = await sdk.getSdkUserId();

const { tempPassword } = await sdk.provisionOrgMemberVaultKey(masterUserRef, {
  multisigId,
  memberId,
  chainType: seat.chainType,
  accountIndex: seat.accountIndex,
  cosignerKeyId: seat.cosignerKeyId,
  walletAddress: seat.walletAddress,
  masterPassword,
});
```

The Master's session is unlocked for the call, because the seat key is re-derived locally from the recovery phrase the seat belongs to. A derived key that does not reproduce the seat's public key is refused and nothing is granted. Hand `tempPassword` over the same way you would for a wallet grant.

Pass `cosignerKeyId` from the seat. When a member holds more than one seat in the vault, the call without it throws `ClientUpgradeRequiredError` rather than guess which seat you meant.

**3. Accept (Employee)**

```typescript theme={null}
const employeeUserRef = await sdk.getSdkUserId();

const { accepted, version } = await sdk.acceptVaultKeyAccess(employeeUserRef, {
  multisigId,
  walletAddress: seat.walletAddress,
  accountIndex: seat.accountIndex,
  memberId,
  tempPassword,
  ownPassword: employeeWalletPassword,
  createEncTokenIfMissing: true,
});
```

Until this lands, the seat's halves are still wrapped under the one-time password, so signing fails exactly as it does for a seat that was never granted. Gate the sign action on the status below rather than letting the employee find out from the error.

Granting several seats, or wallets and seats together, is one call each:

```typescript theme={null}
await sdk.provisionOrgMemberVaultKeyBatch(masterUserRef, { vaults: seats, masterPassword });
await sdk.provisionOrgMemberAccessBatch(masterUserRef, { wallets, vaultKeys, masterPassword });
```

Both wrap everything under a single `tempPassword`, so the Master hands over one secret instead of one per grant. A seat that fails is reported in the results rather than aborting the rest.

### Reading grant status

```typescript theme={null}
// Employee: every vault they sit in
const { items } = await sdk.listMyVaultKeyAccess();
// items: { multisigId, status, walletAddress?, accountIndex? }[]

// Master: one member's vaults, with per-seat detail
const { vaults } = await sdk.listOrgMemberVaults({ memberId });
// each row carries vaultKeyAccess plus vaultSeats[]
```

| `status` | Meaning |
| - | - |
| `not_required` | Not a cosigner-pool chain, or the signer derives the seat key from their own recovery phrase |
| `none` | The seat exists and was never granted |
| `pending` | Granted, not yet accepted. Signing still fails |
| `accepted` | The employee can sign |

On a vault row, `vaultKeyAccess` aggregates the member's seats worst first, so a single `pending` seat keeps the whole vault at `pending`.

### Signing

Pass `derivationSource` from the wallet listing into `signOrder`:

```typescript theme={null}
await sdk.signOrder({ ...signInput, derivationSource: wallet.derivationSource, userRef });
```

It tells the SDK where the signing key comes from. `'seed'` means this signer derives it from their own recovery phrase, which is how the seat's owner signs. Any other value means they do not hold that phrase, so the key can only be rebuilt from the seat's shares, and that is what an employee's org wallet comes back as.

An omitted value is read as the share path, since a caller that resolved nothing has not claimed ownership. Pass the value through from the listing rather than hardcoding it or leaving it out, or the seat's owner takes the employee's route and gets a seat error instead of a signature.

Send `walletPassword` on the same call whenever you have it:

```typescript theme={null}
await sdk.signOrder({ ...signInput, derivationSource: wallet.derivationSource, walletPassword, userRef });
```

A device that accepted the seat somewhere else, or one whose share store does not survive a reload, holds no local half of the seat key. With the password the SDK rebuilds that half from the two halves the server keeps and signs. Without it the call raises `Device share not on device; wallet password required for recovery`, and you prompt for the password and retry.

## Organization orders

Org orders take the same inputs and return the same shapes as personal orders. Only the organization role checks differ.

So every order method takes the same `walletScope`:

```typescript theme={null}
const { order } = await sdk.createSendOrder({
  chain: EChainType.EVM,
  multisigId: vault.id,
  walletAddress: myOrgWallet,
  instructions: [{ to: recipient, amount: '0.5' }],
  walletScope: 'organization',
});

await sdk.signOrder({ ...signInput, walletScope: 'organization', userRef });
await sdk.executeOrder({ ...execInput, walletScope: 'organization', userRef });

const orders = await sdk.getOrders({ multisigId: vault.id, walletScope: 'organization' });
```

Every personal order method has an exact organization counterpart, reached with
`walletScope: 'organization'`. See [Orders](https://sdk-guide.mintlify.site/sdk-guide/orders.md).

> [!WARNING]
>
> Unassigning a wallet from a member, or removing the member, retracts their signatures on open org orders and recomputes the affected orders. An order that was one signature short of its threshold can silently drop further away from it after a revocation. See [Organization](https://sdk-guide.mintlify.site/sdk-guide/organization.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.