SDK
keepkey-vault-sdk is a typed TypeScript client for the KeepKey REST API. Zero dependencies, native fetch, works in browser, Node, Bun, and edge runtimes.
- npm:
keepkey-vault-sdk - Full API reference: /docs/reference/api (auto-generated from the source)
API surface
Every method is grouped by domain on the KeepKeySdk instance:
| Namespace | What it does |
|---|---|
sdk.system | Device info, health, PIN management, initialization, firmware |
sdk.address | Derive addresses (BTC, ETH, Cosmos, Osmosis, Solana, XRP, TRON, …) |
sdk.eth | Ethereum signing — tx, message, EIP-712 typed data |
sdk.btc | Bitcoin / UTXO transaction signing |
sdk.cosmos | Cosmos Hub amino signing — transfer, staking, IBC |
sdk.osmosis | Osmosis amino signing — transfer, staking, IBC, LP, swap |
sdk.thorchain | THORChain transfer and deposit |
sdk.mayachain | MAYAChain transfer and deposit |
sdk.ripple | XRP transaction signing |
sdk.binance | BNB Beacon Chain signing |
sdk.solana | Solana transaction signing (incl. SPL tokens) |
sdk.tron | TRON signing (incl. TRC-20) |
sdk.ton | TON signing (incl. Jettons) |
sdk.xpub | Extended public key derivation — single and batch |
sdk.chain | Portfolio balances, market info, UTXOs, tx history, swap quotes |
sdk.sweep | Recover BTC from non-standard derivation paths |
Creating an instance
import { KeepKeySdk } from 'keepkey-vault-sdk'
const sdk = await KeepKeySdk.create({
apiKey: process.env.KEEPKEY_API_KEY, // optional — if omitted, triggers pairing
serviceName: 'My App',
serviceImageUrl: 'https://example.com/icon.png',
baseUrl: 'http://localhost:1646', // default — override if running on a custom port
})create() verifies the REST API is reachable, validates the supplied apiKey (or initiates pairing), and returns a connected instance. It throws SdkError if the desktop application isn’t running.
Deriving addresses
Every chain has a get{Chain}Address method that takes a BIP32 path. Pass show_display: true to make the device show the address on-screen so the user can verify it before use.
const { address } = await sdk.address.utxoGetAddress({
address_n: [0x80000054, 0x80000000, 0x80000000, 0, 0], // m/84'/0'/0'/0/0 — Native SegWit
coin: 'Bitcoin',
script_type: 'p2wpkh',
show_display: true,
})const { address } = await sdk.address.cosmosGetAddress({
address_n: [0x8000002C, 0x80000076, 0x80000000, 0, 0],
show_display: false,
})Batch public key derivation
When deriving many addresses at once (e.g. for a portfolio view), use the batch endpoint. The server caches results, so repeated calls for the same path are fast.
const { pubkeys } = await sdk.xpub.getPublicKeys([
{ address_n: [0x80000054, 0x80000000, 0x80000000], coin: 'Bitcoin', script_type: 'p2wpkh' },
{ address_n: [0x8000002C, 0x8000003C, 0x80000000], coin: 'Ethereum', type: 'address' },
{ address_n: [0x8000002C, 0x80000076, 0x80000000], coin: 'Cosmos' },
])Signing a transaction
Every signing call blocks until the user approves on the device. The default timeout is 10 minutes.
Ethereum (EIP-1559)
const signed = await sdk.eth.ethSignTransaction({
addressNList: [0x8000002C, 0x8000003C, 0x80000000, 0, 0],
to: '0x...',
value: '0x2386f26fc10000', // 0.01 ETH in wei
data: '0x',
chainId: 1,
nonce: '0x0',
gasLimit: '0x5208',
maxFeePerGas: '0x2540be400',
maxPriorityFeePerGas: '0x3b9aca00',
})
// Broadcast signed.serializedTx via your own RPC providerBitcoin
Bitcoin (and other UTXO chains) require you to supply the full set of inputs and outputs. Each input needs the previous transaction it spends from — you fetch that from a block explorer or indexer.
Get a Bitcoin address
BIP44 derivation paths are encoded as address_n — an array of 32-bit integers with the hardened bit set on the first three levels.
// BIP44 path: m/44'/0'/0'/0/0 — first legacy Bitcoin address
const { address } = await sdk.address.utxoGetAddress({
address_n: [
0x80000000 + 44, // BIP44 purpose (legacy)
0x80000000 + 0, // Coin type: Bitcoin
0x80000000 + 0, // Account 0
0, // External chain
0, // Address index 0
],
coin: 'Bitcoin',
script_type: 'p2pkh', // legacy
show_display: true, // verify on device before using
})Script type maps to address format:
script_type | Format | Example path |
|---|---|---|
p2pkh | Legacy (1…) | m/44'/0'/0'/0/0 |
p2sh | Nested SegWit (3…) | m/49'/0'/0'/0/0 |
p2wpkh | Native SegWit (bc1…) | m/84'/0'/0'/0/0 |
Sign a Bitcoin transaction
// Inputs describe what you're spending. Each input references a previous tx
// by txid + vout index, and must include the full previous transaction so
// the device can verify the input amount.
const inputs = [
{
addressNList: [0x80000000 + 44, 0x80000000 + 0, 0x80000000 + 0, 0, 0],
scriptType: 'p2pkh',
amount: '390000', // amount of this input, in satoshis
vout: 0,
txid: 'd5f65ee80147b4bcc70b75e4bbf2d7382021b871bd8867ef8fa525ef50864882',
tx: prevTx, // the full previous transaction object, fetched from an indexer
},
]
// Outputs describe where the funds are going.
const outputs = [
{
address: '1MJ2tj2ThBE62zXbBYA5ZaN3fdve5CPAz1',
addressType: 'spend',
amount: '380000', // 390_000 input - 10_000 fee
isChange: false,
},
]
const signed = await sdk.btc.btcSignTransaction({
coin: 'Bitcoin',
inputs,
outputs,
version: 1,
locktime: 0,
})
// signed.serializedTx is the hex-encoded transaction, ready to broadcastThe same flow works for other UTXO chains — set coin to 'Litecoin', 'Dogecoin', 'BitcoinCash', etc., and use the matching coin type and script type.
Error handling
The SDK throws SdkError with an HTTP status code for REST errors:
import { KeepKeySdk, SdkError } from 'keepkey-vault-sdk'
try {
const sdk = await KeepKeySdk.create({ serviceName: 'My App' })
} catch (e) {
if (e instanceof SdkError) {
if (e.status === 503) {
console.error('Desktop application is not running')
} else if (e.status === 401) {
console.error('Pairing was rejected')
}
}
throw e
}Common status codes:
| Status | Meaning |
|---|---|
200 | Success |
401 | Missing, invalid, or revoked API key |
403 | User rejected the signing request on the device |
408 | Signing request timed out (device didn’t respond within 10 min) |
503 | Desktop application not reachable |