Blockstream Enterprise Custody SDK
RecipesWallets

Wallet Onboarding Workflow

How to create and configure wallets in Blockstream Enterprise Custody, from signer creation through address generation.

Complete Setup and Installation first.

This guide assumes:

  • an authenticated user that can create signers and wallets
  • the request helper from Making Requests
  • uuid for the example IDs

Why signers are separate from wallets

The platform separates key custody from wallet configuration on purpose.

  • A signer owns or references key material
  • A wallet defines how those signers are used on-chain
  • A wallet can reference one or more signers depending on its type

That separation makes several things easier:

  • signer rotation without redesigning the wallet model
  • clearer audits of who controls which signing path
  • multi-signer wallet construction from independently managed signers
  • reuse of the same signer in more than one wallet context

Standards context

Relevant external references:

  • BIP-32: hierarchical deterministic key derivation
  • BIP-39: mnemonic seed phrases
  • BIP-87: descriptor-oriented derivation conventions

Wallet onboarding flow

Every wallet flow follows the same high-level sequence:

  1. create a signer
  2. derive an XPUB
  3. create the wallet
  4. generate addresses

Step 1: Create a signer

import { v4 as uuidv4 } from 'uuid'

const signerResult = await broadcastRequest({
  action: 'add',
  resource: '/signers',
  details: {
    sid: uuidv4(),
    name: 'ops-signer',
    location: 'hosted',
  },
})

const signerId = signerResult.details.sid

Signer locations

LocationMeaning
hostedKeys are managed by the Custody Engine
externalKeys are managed elsewhere

If your environment does not yet support external signers in production, stay with hosted.

Step 2: Derive an XPUB

const xpubResult = await broadcastRequest({
  action: 'add',
  resource: `/signers/${signerId}/xpubs`,
  details: {
    id: uuidv4(),
    change: 'receive',
  },
})

const keyoriginXpub = xpubResult.details.keyorigin_xpub

XPUB derivation matters because it lets the wallet generate receive and change addresses without exposing private signing material.

Step 3: Create the wallet

const walletResult = await broadcastRequest({
  action: 'add',
  resource: '/wallets',
  details: {
    id: uuidv4(),
    name: 'treasury-wallet',
    type: 'single_sig',
    network: 'bitcoin',
    value: { signer_id: signerId },
  },
})

const walletId = walletResult.details.wid

Step 4: Generate an address

const addressResult = await broadcastRequest({
  action: 'add',
  resource: `/wallets/${walletId}/addresses`,
  details: {
    type: 'receive',
    index: 1,
  },
})

const address = addressResult.details.address

Choosing the right wallet flow

On this page