Skip to content

Manage keys

A vault is one MPC key. This is about the keys that hold funds — for the API keys that authenticate your requests, see Authentication.

const vault = await xkova.createVault({ tier: "self", name: "treasury" });

Key generation runs across the parties and is asynchronous: a new vault returns creating and becomes active shortly after. Poll getVault(id) until status === "active" before deriving wallets or signing.

The tier is fixed at creation and decides where shares live and who must authorize a signature:

TierUse it when
selfYou want true self-custody — XKOVA cannot sign without your required share. Best for treasury and operational funds you control.
hostedYou are giving your users embedded wallets — each signature is gated by that user’s token.
customer_teeYou run a signing party in your own trusted environment.

You cannot change a vault’s tier later — create a new vault instead. See How it works for the full model.

One vault backs many addresses. Each createWallet derives a new address under the vault’s key on a chain:

const evm = await xkova.createWallet(vault.id, { chain: "avalanche_c" });
const btc = await xkova.createWallet(vault.id, { chain: "bitcoin" });

All wallets under a vault share its key material and its controls (policy, limits). Deriving is instant. Pass account to derive a distinct address on the same chain.

Vault shares can be re-randomized on a schedule or on demand. During a rotation the vault reports resharing (or reshare_pending); the address is unchanged, so funds, allowlists, and integrations are unaffected. There is no window in which a full private key exists — old shares stop working, new ones take over. Signing pauses briefly while resharing completes.

A vault can be frozen to halt all signing — a kill switch for an incident. While frozen, every signing request is refused; funds do not move. It is reversible once the cause is resolved.

Every vault keeps a tamper-evident history — creation, activation, policy and limit changes, every rotation, and every authorized signature — where each entry is hash-chained to the previous one, so any gap or edit is detectable:

const audit = await xkova.request("GET", `/v1/vaults/${vault.id}/audit`);

Use it for compliance evidence and for reconciling what was signed against what you intended.

StatusMeaning
creatingkey generation in progress
activeready to derive wallets and sign
resharing / reshare_pendingrotation in progress; address unchanged
frozensigning halted