Blockstream Enterprise Custody SDK
Onboarding

API User Onboarding Workflow

How to onboard an API user to Blockstream Enterprise Custody, capture the returned device credentials, and initialize the SDK.

Complete Setup and Installation before following this guide.

This guide uses:

  • @blockstream/cryptic
  • @blockstream/cryptic/utils
  • @blockstream/ecs-registry
  • uuid

You also need:

  • a user that can create API users
  • access to the /users endpoint
  • an authenticated Blockstream session that can create users
  • a Broadcaster for that session, or one of the lower-level request helpers from Making Requests

If your deployment requires mTLS for /request, follow the same onboarding flow here and switch the broadcaster transport to the Node-side mTLS pattern described in Making Requests.

What this flow does

API-user onboarding is the programmatic path for machine-to-machine integrations. In this flow you:

  1. generate the client's key material locally
  2. create the user on the server
  3. capture the returned device_uuid, server_ecdsa_pubkey, and server_rsa_pubkey
  4. initialize Blockstream with those values

For the core crypto model and installation details, use Setup and Installation. This page stays focused on the onboarding sequence itself.

Step 1: Generate client key pairs

Use the built-in helper unless you need a custom key-management flow:

import { generateUserKeyPairs } from '@blockstream/cryptic/utils'

const userKeys = await generateUserKeyPairs()

Step 2: Create the API user

Create the user with the generated public keys. In most applications, this means using a Broadcaster built from the already-authenticated admin or service session that is provisioning the new API user:

import { v4 as uuidv4 } from 'uuid'
import { Broadcaster } from '@blockstream/cryptic/broadcaster'

const adminBroadcaster = new Broadcaster(ECS_BASE_URL, adminClient)

const createResponse = await adminBroadcaster.processBroadcast({
  action: 'add',
  resource: '/users',
  details: {
    uid: uuidv4(),
    email: 'api_user@example.com',
    first_name: 'API',
    last_name: 'User',
    ecdsa_pubkey: userKeys.ecdsa.publicKeyHex,
    rsa_pubkey: userKeys.rsa.publicKeyPem,
    type: 'api',
  },
})

On success, the server returns the values you need to initialize the SDK:

const { uid, device_uuid, server_ecdsa_pubkey, server_rsa_pubkey } = createResponse.details

Why these returned fields matter

  • device_uuid is the server-generated device identity for this API user
  • server_ecdsa_pubkey is the public key used to verify server signatures
  • server_rsa_pubkey is the public key used to encrypt request material for the server

Persist device_uuid with the user's private keys. They are a matched credential set.

Step 3: Initialize the SDK

import { Buffer } from 'buffer'
import { Blockstream } from '@blockstream/cryptic'
import { Broadcaster } from '@blockstream/cryptic/broadcaster'

const userClient = new Blockstream(
  userKeys.rsa.privateKeyPem,
  Buffer.from(userKeys.ecdsa.privateKeyHex, 'hex'),
  device_uuid,
  server_rsa_pubkey,
  Buffer.from(server_ecdsa_pubkey, 'hex'),
)

const userBroadcaster = new Broadcaster(ECS_BASE_URL, userClient)

Step 4: Verify the user can make authenticated requests

const response = await userBroadcaster.processBroadcast({
  action: 'get',
  resource: `/users/${uid}`,
})

If this succeeds, the onboarding flow is complete.

If your environment requires mTLS, keep this verification step the same at the ECS message level and only replace the broadcaster transport layer with the mTLS flow from Making Requests.

Complete example

import { Buffer } from 'buffer'
import { Blockstream } from '@blockstream/cryptic'
import { Broadcaster } from '@blockstream/cryptic/broadcaster'
import { generateUserKeyPairs } from '@blockstream/cryptic/utils'
import { v4 as uuidv4 } from 'uuid'

async function onboardApiUser(
  adminClient: Blockstream,
  email: string,
  firstName: string,
  lastName: string,
) {
  const userKeys = await generateUserKeyPairs()
  const adminBroadcaster = new Broadcaster(ECS_BASE_URL, adminClient)

  const createResponse = await adminBroadcaster.processBroadcast({
    action: 'add',
    resource: '/users',
    details: {
      uid: uuidv4(),
      email,
      first_name: firstName,
      last_name: lastName,
      ecdsa_pubkey: userKeys.ecdsa.publicKeyHex,
      rsa_pubkey: userKeys.rsa.publicKeyPem,
      type: 'api',
    },
  })

  if (createResponse.status !== 'success') {
    throw new Error(`User creation failed: ${createResponse.message}`)
  }

  const { uid, device_uuid, server_ecdsa_pubkey, server_rsa_pubkey } = createResponse.details

  const client = new Blockstream(
    userKeys.rsa.privateKeyPem,
    Buffer.from(userKeys.ecdsa.privateKeyHex, 'hex'),
    device_uuid,
    server_rsa_pubkey,
    Buffer.from(server_ecdsa_pubkey, 'hex'),
  )

  const broadcaster = new Broadcaster(ECS_BASE_URL, client)

  await broadcaster.processBroadcast({
    action: 'get',
    resource: `/users/${uid}`,
  })

  return {
    uid,
    deviceUuid: device_uuid,
    serverEcdsaPublicKey: server_ecdsa_pubkey,
    serverRsaPublicKeyPem: server_rsa_pubkey,
    keys: userKeys,
  }
}

Troubleshooting

IssueLikely causeWhat to check
unauthorized responseWrong device or keysConfirm the stored device_uuid matches the key pair
Signature verification failsWrong server ECDSA key formatConfirm server_ecdsa_pubkey is passed as hex bytes
Decryption failsWrong RSA private keyConfirm the stored PEM matches the created user
Missing device_uuidUser creation did not complete successfullyInspect the create-user response status and message

On this page