Skip to Content

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.

API surface

Every method is grouped by domain on the KeepKeySdk instance:

NamespaceWhat it does
sdk.systemDevice info, health, PIN management, initialization, firmware
sdk.addressDerive addresses (BTC, ETH, Cosmos, Osmosis, Solana, XRP, TRON, …)
sdk.ethEthereum signing — tx, message, EIP-712 typed data
sdk.btcBitcoin / UTXO transaction signing
sdk.cosmosCosmos Hub amino signing — transfer, staking, IBC
sdk.osmosisOsmosis amino signing — transfer, staking, IBC, LP, swap
sdk.thorchainTHORChain transfer and deposit
sdk.mayachainMAYAChain transfer and deposit
sdk.rippleXRP transaction signing
sdk.binanceBNB Beacon Chain signing
sdk.solanaSolana transaction signing (incl. SPL tokens)
sdk.tronTRON signing (incl. TRC-20)
sdk.tonTON signing (incl. Jettons)
sdk.xpubExtended public key derivation — single and batch
sdk.chainPortfolio balances, market info, UTXOs, tx history, swap quotes
sdk.sweepRecover 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.

Get Bitcoin address
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,
})
Get Cosmos address
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 provider

Bitcoin

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_typeFormatExample path
p2pkhLegacy (1…)m/44'/0'/0'/0/0
p2shNested SegWit (3…)m/49'/0'/0'/0/0
p2wpkhNative 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 broadcast

The 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:

StatusMeaning
200Success
401Missing, invalid, or revoked API key
403User rejected the signing request on the device
408Signing request timed out (device didn’t respond within 10 min)
503Desktop application not reachable
Last updated on