HiveXPH SDKhivexph-sdk
NPM
Developer toolkit for Hive Custom JSON and Hive Engine transactions.

Reference

API reference

One client, one namespace per concern, one block stream. Every module is reachable from a HiveClient instance and every configuration context exposes the same surface.

HiveClient

TypeScript
import { HiveClient } from "hivexph-sdk";

const hive = new HiveClient({
  endpoint: "https://api.hive.blog",   // optional, Beacon picks a node otherwise
  beaconUrl: "https://beacon.peakd.com/api/nodes",
  applicationId: "my-application",  // default custom_json id
  accounts: {
    treasury: { account: "my-account", keyEnv: "TREASURY_ACTIVE_KEY" },
  },
  tests: {
    accounts: { minter: "test-minter" },  // your own structure
  },
});

hive.endpoint;                     // resolved RPC endpoint
hive.accounts.treasury;            // key-free account reference
hive.configs.tests.accounts.minter; // your configuration, typed

Namespaces

hive.*
ParameterTypeRequiredDescription
configsHiveConfigs<TConfig>NoThe configuration object you passed, frozen and fully typed.
accountsRecord<string, AccountReference>NoKey-free account references built from configs.accounts.
rpcRpcClientNoRaw JSON-RPC access, block reads and broadcasting.
beaconBeaconClientNoNode discovery and health scoring.
builderCustomJsonBuilderNoBuild standardized custom_json operations.
parserCustomJsonParserNoExtract and validate custom_json payloads.
keychainKeychainClientNoBrowser signing through the Hive Keychain extension.
blocksBlockWatcherNoThe core block stream as a raw async iterator.
customJsonCustomJsonWatcherNoparse() plus a filtered view of the core stream.
paymentsPaymentClientNoNative HIVE/HBD and Layer 2 payments, parse, validate, watch.
readerReaderClientNoSingle transaction reads and the unified multi-filter stream.
issuerIssuerClientNoBackend Hive Engine token and NFT operations.

There is exactly one blockchain streaming engine. hive.blocks.watch(), hive.customJson.watch(), hive.payments.watch() and hive.reader.stream() are all consumers or dispatch layers over that same core block reader — never independent polling loops.

Full method map

The complete public surface, grouped by namespace.

TypeScript
// ── configuration ────────────────────────────────────────────────
hive.configs                  // your configuration object, frozen
hive.account(alias) | listAccounts() | resolveAccount(alias)
hive.endpoint

// ── blockchain access ────────────────────────────────────────────
hive.rpc.call(method, params, options?) -> Promise<T>
hive.rpc.getDynamicGlobalProperties(options?) | getHeadBlockNumber(options?)
hive.rpc.getBlock(blockNumber, options?) | resetEndpoint()
hive.beacon.getNodes(options?) | getHealthyNodes(options?) | getBestEndpoint(options?)

// ── frontend signing (Hive Keychain) ─────────────────────────────
hive.keychain.isAvailable() -> boolean
hive.keychain.requestSignIn({ username, message, authority? })
hive.keychain.customJson({ username, id, action, metadata?, authority?, message? })
hive.keychain.customJsonRaw({ username, id, json, authority?, message? })
hive.keychain.requestTransfer({ username, to, amount, currency, memo, enforce? })
  // currency "HIVE" | "HBD" -> native transfer; any token symbol -> Hive Engine token transfer
hive.keychain.payments.hive.transfer({ username, account, amount, symbol, action, metadata?, message? })
hive.keychain.payments.engine.transfer({ username, account, symbol, quantity, action, metadata?, message? })
hive.keychainIssuer.token.issue | transfer | burn      (+ buildIssue | buildTransfer | buildBurn)
hive.keychainIssuer.token.create({ username, symbol, name, precision, maxSupply, url?, skipChecks? })
  // checks BEE balance vs the creation fee and that the symbol is free before signing
hive.keychainIssuer.token.checkCreate({ username, symbol })  -> TokenCreationCheck (read-only)
hive.keychainIssuer.token.buildCreate(input)                 -> offline payload preview
hive.keychainIssuer.nft.issue | issueMultiple | transfer | burn  (+ build* variants)
hive.keychainIssuer.nft.create({ username, name, symbol, orgName?, productName?,
                                 maxSupply?, website?, authorizedIssuingAccounts?,
                                 authorizedIssuingContracts?, skipChecks? })
hive.keychainIssuer.nft.checkCreate({ username, symbol })    -> NftCreationCheck (read-only)
hive.keychainIssuer.nft.buildCreate(input)                   -> offline payload preview

// ── streaming: ONE engine, four views ────────────────────────────
hive.blocks.watch(options)          -> AsyncGenerator<NormalizedBlock>
hive.customJson.parse(operation, options?) -> CustomJsonEvent | null
hive.customJson.watch(options)      -> AsyncGenerator<CustomJsonEvent>
hive.payments.watch(options)        -> AsyncGenerator<ParsedPayment>
hive.reader.stream(options)         -> StreamEngine
  engine.customJson(filter) | payment(filter) | onEvent(handler) -> StreamSubscription
  engine.start() | stop() | pause() | resume() | clearFilters()

// ── reading ──────────────────────────────────────────────────────
hive.reader.transaction({ transactionId, id?, actions? }) -> TransactionResult

// ── payments ─────────────────────────────────────────────────────
hive.payments.parse({ transactionId })              -> ParsedPayment[]
hive.payments.validate({ transactionId, expected? }) -> ParsedPayment
hive.payments.hive.build(input) | transfer(input)    // native HIVE / HBD
hive.payments.engine.build(input) | transfer(input)  // Layer 2 tokens
hive.payments.engineRpc.getTransactionInfo(id) | findOne(params)

// ── issuing (backend, signed with a resolved key) ────────────────
hive.issuer.token.issue | transfer | burn   (+ buildIssue | buildTransfer | buildBurn)
hive.issuer.token.create({ from, symbol, name, precision, maxSupply, url?, skipChecks? })
hive.issuer.token.checkCreate(input) | buildCreate(input)   // input takes { from, symbol, … }
hive.issuer.nft.issue | issueMultiple | transfer | burn (+ build* variants) | countInstances
hive.issuer.nft.create({ from, name, symbol, orgName?, productName?, maxSupply?,
                         website?, authorizedIssuingAccounts?, authorizedIssuingContracts?,
                         skipChecks? })
hive.issuer.nft.checkCreate(input) | buildCreate(input)     // input takes { from, symbol, … }
// burn is implemented as a transfer to "null"

hive.configs

TypeScript
hive.configs;                      // exactly what you passed, frozen
hive.configs.tests.accounts.minter; // your own nested structure

hive.listAccounts();               // ["treasury"]
hive.account("treasury");          // AccountReference (no keys)
hive.resolveAccount("treasury");   // { alias, account }

hive.rpc / hive.beacon

TypeScript
const head = await hive.rpc.getHeadBlockNumber();
const props = await hive.rpc.getDynamicGlobalProperties();
const block = await hive.rpc.getBlock(head);

// Any node method, typed by the caller.
const [account] = await hive.rpc.call<Account[]>(
  "condenser_api.get_accounts",
  [["alice"]],
);

// Node discovery. resetEndpoint() clears a sticky failed node.
const nodes = await hive.beacon.getHealthyNodes();
const best = await hive.beacon.getBestEndpoint();
hive.rpc.resetEndpoint();

hive.keychain

TypeScript
if (!hive.keychain.isAvailable()) throw new Error("Install Hive Keychain");

const signIn = await hive.keychain.requestSignIn({
  username: "alice",
  message: "Sign in to my-application: nonce-123456",
  authority: "posting",
});

// Standardized { action, metadata } payload.
await hive.keychain.customJson({
  username: "alice",
  id: "my-application",
  action: "claim_reward",
  metadata: { questId: 42 },
  authority: "posting",      // "posting" | "active"
  message: "Claim your reward",
});

// Raw, already-serialized body — broadcast verbatim.
await hive.keychain.customJsonRaw({
  username: "alice",
  id: "my-application",
  json: JSON.stringify({ action: "ping", metadata: null }),
});

// Native transfer with a standardized trigger in the memo.
await hive.keychain.requestTransfer({
  username: "alice",
  to: "treasury",
  amount: "1.000",
  currency: "HIVE",             // "HIVE" | "HBD" -> Layer 1 transfer
  memo: JSON.stringify({ action: "buy_pack", metadata: { packs: 1 } }),
  enforce: true,
});

// Layer 2 token transfer — same call, just use the token symbol as currency.
await hive.keychain.requestTransfer({
  username: "alice",
  to: "treasury",
  amount: "5",
  currency: "SCRAP",            // any Hive Engine symbol -> token transfer
  memo: JSON.stringify({ action: "buy_pack", metadata: { packs: 1 } }),
});

// Layer 2 contract actions signed by the user's own keys (custom_json).
await hive.keychainIssuer.token.transfer({
  username: "alice",
  symbol: "SCRAP",
  account: "bob",
  quantity: "5",
});

Keychain sign-in

Added in 1.2.0. requestSignIn() asks Hive Keychain to sign a challenge message with the account's key, so your backend can verify the signature and issue a session. Use a server-issued nonce in the message to prevent replays.

KeychainSignInInput
ParameterTypeRequiredDescription
usernamestring (required)NoHive account signing in.
messagestring (required)NoChallenge to sign. Include a server-issued nonce.
authority"posting" | "active"NoKey used for the signature. Defaults to "posting".
KeychainSignInResult
ParameterTypeRequiredDescription
usernamestringNoAccount that signed.
messagestringNoExact challenge that was signed.
authority"posting" | "active"NoKey authority used.
signaturestringNoSignature to verify server-side.
timestampnumberNoClient timestamp of the signature.
rawKeychainResponseNoUntouched Keychain response.
TypeScript
const { nonce } = await fetch("/api/auth/nonce").then((r) => r.json());

const signIn = await hive.keychain.requestSignIn({
  username: "alice",
  message: `Sign in to my-application: ${nonce}`,
  authority: "posting",
});

await fetch("/api/auth/login", {
  method: "POST",
  body: JSON.stringify({
    username: signIn.username,
    message: signIn.message,
    signature: signIn.signature,
  }),
});

Token & NFT creation

Added in 1.2.0. Both a token and an NFT cost 100 BEE to create on Hive Engine and both symbols are unique on the sidechain, so every create() runs a preflight first: the signing account must hold the fee in BEE and the symbol must still be free. A failure throws INSUFFICIENT_BEE, TOKEN_ALREADY_EXISTS or NFT_ALREADY_EXISTS before Keychain opens or a key is resolved. Pass skipChecks: true to opt out — creation fees are non-refundable.

The same methods exist on both issuers: hive.keychainIssuer (browser, signed by the user) and hive.issuer (backend, signed with a resolved key).

Token creation — tokens.create
ParameterTypeRequiredDescription
namestring (required)NoDisplay name, up to 50 letters, digits and spaces.
symbolstring (required)NoUppercase letters only, up to 10 characters.
precisionnumber (required)NoDecimal places, 0 to 8.
maxSupplystring (required)NoPositive decimal string. Never a float.
urlstringNoOptional website. Editable later in the TribalDex Token Manager.
skipChecksbooleanNoSkips the BEE / symbol preflight. Off by default.
NFT creation — nft.create
ParameterTypeRequiredDescription
namestring (required)NoDisplay name, up to 50 letters, digits and spaces.
symbolstring (required)NoUppercase letters only, up to 10 characters.
orgName / productNamestringNoOptional organization and product names.
maxSupplystringNoOptional positive decimal string. Unlimited when omitted.
websitestringNoOptional project website.
authorizedIssuingAccountsstring[]NoOptional accounts allowed to issue instances.
authorizedIssuingContractsstring[]NoOptional contracts allowed to issue instances.
skipChecksbooleanNoSkips the BEE / symbol preflight. Off by default.
TokenCreationCheck / NftCreationCheck
ParameterTypeRequiredDescription
fee / balancestringNoBEE required by the sidechain and the account's liquid BEE balance.
hasEnoughBeebooleanNoBalance covers the fee.
symbolExistsbooleanNoThe symbol is already taken.
existingToken / existingNftEngineTokenRow | EngineNftRow | nullNoThe existing row when the symbol is taken.
okbooleanNoTrue only when the fee is affordable and the symbol is free.
issuesstring[]NoHuman-readable reasons creation would fail.
TypeScript
// ── Keychain (browser, user signs with their Active key) ─────────
const check = await hive.keychainIssuer.token.checkCreate({
  username: "alice",
  symbol: "SCRAP",
});
check.ok; check.fee; check.balance; check.issues;

await hive.keychainIssuer.token.create({
  username: "alice",
  name: "Scrap Token",
  symbol: "SCRAP",
  precision: 3,
  maxSupply: "1000000",
  url: "https://scrap.gg",   // optional
});

await hive.keychainIssuer.nft.create({
  username: "alice",
  name: "Raider Cards",
  symbol: "CARD",
  orgName: "Idle Raiders",   // optional
  maxSupply: "10000",        // optional
});

// ── Backend (signed with a resolved key) ─────────────────────────
const preview = hive.issuer.token.buildCreate({
  symbol: "SCRAP",
  name: "Scrap Token",
  precision: 3,
  maxSupply: "1000000",
});   // offline payload, no network

await hive.issuer.token.create({
  from: hive.accounts.treasury,
  name: "Scrap Token",
  symbol: "SCRAP",
  precision: 3,
  maxSupply: "1000000",
});

await hive.issuer.nft.create({
  from: hive.accounts.treasury,
  name: "Raider Cards",
  symbol: "CARD",
});

hive.blocks.watch()

The raw core stream. Every other streaming API is built on it, so use this only when you need untouched blocks.

BlockStreamOptions
ParameterTypeRequiredDescription
fromBlocknumberNoFirst block to read. Defaults to the head block (live).
signalAbortSignalNoStops the iterator cleanly.
pollIntervalMsnumberNoPoll interval while waiting for new blocks. Default 3000.
onError(error, blockNumber) => voidNoCalled on recoverable RPC errors instead of throwing.
maxRetriesPerBlocknumberNoConsecutive failures on one block before throwing. Default 5.
TypeScript
const controller = new AbortController();

for await (const block of hive.blocks.watch({
  fromBlock: 109_542_165,
  pollIntervalMs: 3000,
  signal: controller.signal,
  onError: (error, blockNumber) => console.warn(blockNumber, error),
})) {
  console.log(block.blockNumber, block.transactions.length);
}

controller.abort();

hive.customJson

CustomJsonStreamOptions (extends BlockStreamOptions)
ParameterTypeRequiredDescription
idstring (required)Nocustom_json id to filter by.
actionsstring[]NoAction allow-list, OR-matched against the payload action.
onInvalidPayload({ reason, blockNumber, raw }) => voidNoCalled when the id matches but the payload breaks the protocol. Never throws.
TypeScript
// One-off parse of a single operation (needs the block position context).
const result = hive.customJson.parse(operation, position, { id: "my-application" });
if (result.status === "ok") console.log(result.event.action, result.event.metadata);

// Filtered async iteration over the core stream.
for await (const event of hive.customJson.watch({
  id: "my-application",
  actions: ["claim_reward", "buy_pack"],   // OR filter
  fromBlock: 109_542_165,
  onInvalidPayload: ({ reason, blockNumber }) => console.warn(blockNumber, reason),
})) {
  console.log(event.account, event.action, event.metadata);
}

hive.payments

PaymentStreamOptions (extends BlockStreamOptions)
ParameterTypeRequiredDescription
filters.fromstringNoSender account.
filters.accountstringNoRecipient account.
filters.symbolstringNoHIVE, HBD or any Layer 2 token symbol.
filters.quantitystringNoExact amount, compared as a decimal string.
filters.actionsstring[]NoTrigger actions, OR-matched. Implies a valid trigger.
filters.requireTriggerbooleanNoOnly emit transfers carrying a valid standardized trigger.
onSuccess(payment) => voidNoVerified payments (success === true).
onFailed(payment) => voidNoPayments whose Layer 2 execution failed.
TypeScript
// Build and send — native HIVE / HBD.
const preview = hive.payments.hive.build({
  from: hive.accounts.treasury,
  account: "bob",             // recipient
  amount: "1.000",            // decimal string
  symbol: "HIVE",             // "HIVE" or "HBD"
  action: "payout",
  metadata: { invoice: "INV-1" },
});
preview.amount; // "1.000 HIVE"
preview.memo;     // serialized trigger
await hive.payments.hive.transfer(preview);

// Build and send — Layer 2 token.
await hive.payments.engine.transfer({
  from: hive.accounts.treasury,
  account: "bob",
  symbol: "SCRAP",
  quantity: "1",
  action: "payout",
  metadata: { invoice: "INV-2" },
});

// Read a transaction's payments without execution checks.
const payments = await hive.payments.parse({ transactionId: "7b064a84…" });

// Verify one payment against expectations.
const result = await hive.payments.validate({
  transactionId: "7b064a84…",
  expected: { account: "rhiaji", symbol: "SCRAP", quantity: "1", action: "buy_pack" },
});
result.status; // "success" | "failed" | "pending" | "invalid" | "not_found"

// Preferred live iteration.
for await (const payment of hive.payments.watch({
  filters: { account: "rhiaji", symbol: "SCRAP", actions: ["buy_pack"] },
  onFailed: (failed) => console.warn("execution failed", failed.transactionId),
})) {
  if (payment.trigger) fulfil(payment.trigger.action, payment.trigger.metadata);
}

// Raw sidechain access when you need it.
await hive.payments.engineRpc.getTransactionInfo("7b064a84…");

hive.reader

UnifiedStreamOptions (extends BlockStreamOptions)
ParameterTypeRequiredDescription
engineConfirmationAttemptsnumberNoRe-reads of a pending Layer 2 execution before giving up. Default 6.
engineConfirmationDelayMsnumberNoDelay between Layer 2 execution reads, in ms. Default 2000.
CustomJsonFilter
ParameterTypeRequiredDescription
idstringNocustom_json id, e.g. "my-game".
actionsstring[]NoOR-matched against the standardized payload action.
standardizedOnlybooleanNoOnly match payloads following the { action, metadata } protocol.
handler(event) => void (required)NoReceives every matching CustomJsonStreamEvent.
PaymentFilter
ParameterTypeRequiredDescription
from / account / symbolstringNoSender, recipient and currency or token symbol.
quantitystringNoDecimal string, compared precision-safely. Never a number.
actionsstring[]NoTrigger actions, OR-matched. Implies a valid trigger.
requireTriggerbooleanNoOnly match transfers carrying a valid standardized trigger.
handler(event) => void (required)NoVerified successful payments only.
onFailed(event) => voidNoMatching payments whose Layer 2 execution failed.
TypeScript
// One transaction, decoded.
const read = await hive.reader.transaction({
  transactionId: "7b064a84…",
  id: "my-application",   // optional custom_json id filter
});
read.blockNumber; read.operations; read.customJson; read.payments; read.nfts; read.invalid;

// One engine, one block reader, many filters.
const stream = hive.reader.stream({
  fromBlock: 109_542_165,
  engineConfirmationAttempts: 6,
  engineConfirmationDelayMs: 2000,
});

const offJson = stream.customJson({
  id: "my-application",
  actions: ["claim_reward"],
  standardizedOnly: true,
  handler: (event) => console.log(event.account, event.action, event.metadata),
});

const offPay = stream.payment({
  account: "rhiaji",
  symbol: "SCRAP",
  actions: ["buy_pack"],
  handler: (event) => fulfil(event.trigger?.action, event.trigger?.metadata),
  onFailed: (event) => console.warn("failed", event.transactionId, event.error),
});

// Firehose of every normalized event produced by the registered filters.
stream.onEvent((event) => {
  if (event.type === "custom_json") console.log(event.id, event.action);
  else console.log(event.transfer.symbol, event.status);
});

await stream.start();
stream.pause();
stream.resume();
offJson.unsubscribe();
offPay();              // subscriptions are callable too
stream.clearFilters();
stream.stop();

hive.issuer

TypeScript
// Offline preview: no key resolution, no network.
const preview = hive.issuer.token.buildTransfer({
  from: hive.accounts.treasury,
  symbol: "SCRAP",
  account: "bob",
  quantity: "5",
});
preview.alias; preview.account; preview.id; preview.json; preview.operation;

// Broadcast variants.
await hive.issuer.token.issue({ from: hive.accounts.treasury, symbol: "SCRAP", account: "bob", quantity: "10" });
await hive.issuer.token.transfer({ from: hive.accounts.treasury, symbol: "SCRAP", account: "bob", quantity: "5" });
await hive.issuer.token.burn({ from: hive.accounts.treasury, symbol: "SCRAP", quantity: "5" }); // transfer to "null"

// NFTs.
await hive.issuer.nft.issue({
  from: hive.accounts.treasury,
  symbol: "CARD",
  account: "bob",
  feeSymbol: "BEE",
  properties: { rarity: "rare" },
});
await hive.issuer.nft.issueMultiple({
  from: hive.accounts.treasury,
  instances: [
    { symbol: "CARD", account: "bob", feeSymbol: "BEE" },
    { symbol: "CARD", account: "carol", feeSymbol: "BEE" },
  ],
});
await hive.issuer.nft.transfer({
  from: hive.accounts.treasury,
  account: "bob",
  nfts: [{ symbol: "CARD", ids: ["1", "2"] }],
});
await hive.issuer.nft.burn({
  from: hive.accounts.treasury,
  symbol: "CARD",
  id: "3", // string | string[]
});
hive.issuer.nft.countInstances({ nfts: [{ symbol: "CARD", ids: ["1", "2"] }] }); // 2

Every result branch

Payments are normalized to one shape for both networks, so branching is the same code path whether the transfer was native HIVE/HBD or a Layer 2 token.

PaymentStatus
ParameterTypeRequiredDescription
successsuccess === trueNoThe transfer and, for Layer 2, its execution succeeded.
failedsuccess === falseNoThe transaction exists but execution failed.
pendingsuccess === nullNoNot yet indexed enough to verify — retried automatically while streaming.
invalidsuccess === falseNoThe transaction exists but does not match expectations.
not_foundsuccess === falseNoThe transaction cannot be found.
TypeScript
switch (payment.status) {
  case "success":   return fulfil(payment);
  case "failed":    return refund(payment);
  case "pending":   return retryLater(payment);
  case "invalid":   return flagMismatch(payment);
  case "not_found": return ignore(payment);
}

// Trigger branches — a bad memo never breaks the stream.
if (payment.trigger) {
  handle(payment.trigger.action, payment.trigger.metadata);
} else {
  // empty, plain-text or malformed memo: surfaced, but not processed
  log("no trigger", payment.transactionId);
}

// Custom JSON branches.
if (event.standardized && event.customJson) {
  handle(event.action, event.metadata);
} else {
  inspectRaw(event.json);   // raw protocol payload, still emitted
}

// Idempotency key for both event kinds.
const key = `${payment.transactionId}:${payment.operationIndex ?? 0}`;