Skip to content

REST Workflows

REST endpoints for workflows

Workflows are declarative sequences of steps executed asynchronously. Results are cached in RocksDB and can be queried multiple times without re-execution.

Execution model:

  • Submitted via POST /workflows, queued immediately
  • Execute asynchronously — microseconds to minutes depending on steps
  • Results cached until explicitly deleted
  • Query results via /pubkeys-set/txns, /pubkeys-set/utxos, or /workflows/{id}/results/{type}

Returns workflows in pending and running states by default — suitable for dashboards and operational monitoring. Finished workflows are retained briefly up to TTL seconds and can be included. See below.

To track completion of a specific workflow use GET /workflows/{id}, or subscribe to workflow.progress events and listen for state: completed.

GET /workflows

Add all finished history or select as desired:

GET /workflows?include=finished // all finished history
GET /workflows?include=completed
GET /workflows?include=cancelled
GET /workflows?include=failed

Response:

[
{
"id": "VdncVtkNg5",
"request_id": "256483",
"name": "Import wallet-1",
"state": "running",
"total_steps": 6,
"elapsed_ms": 12,
"current_step_status": {
"step": 3,
"progress": 45.0
},
"steps": [
{"step": "pubkeys.extpubkey.import"},
{"step": "pubkeys.scripthash.subscribe"},
{"step": "pubkeys.scripthash.history"},
{"step": "pubkeys.transaction.fetch"},
{"step": "blockheaders.unresolved.fetch"},
{"step": "pubkeys.unspent.compute"}
]
}
]

Response fields:

FieldDescription
idWorkflow ID
request_idEchoed from workflow submission. Absent if not set
nameEchoed from workflow submission. Absent if not set
stateWorkflow state — See table below.
total_stepsTotal number of steps in the workflow
current_step_statusPer-step detail — see below. Present only when state is running
pending_msMilliseconds the workflow has been waiting in the queue. Present only when state is pending
elapsed_msMilliseconds the workflow has been executing. Present only when state is running
stepsArray of step objects, each containing a step name as listed in the Step Reference. Present only when state is running or pending

Workflow states:

StateDescription
pendingQueued, not yet executing
runningCurrently executing
completedFinished successfully
cancelledStopped before completion
failedTerminated with an error

current_step_status fields:

FieldDescription
step1-based index of the currently executing step
progressPercent complete within the current step (0–100)

The current_step_status object appears in both REST responses and workflow.progress WebSocket events. See workflow.progress for the event schema.

step fields:

FieldDescription
step1-based position of this step in the workflow
nameStep name as listed in the Step Reference

Returns a single workflow by ID. The primary interface for polling workflow completion.

GET /workflows/WTjV1t4G

Response shape is identical to items in GET /workflows.

Completion detection:

Poll until state is completed, then fetch results via POST /pubkeys-set/txns, POST /pubkeys-set/utxos, or GET /workflows/{id}/results/core.

while (true) {
const workflow = await get(`/workflows/${id}`)
if (workflow.state === 'completed') break
if (workflow.state === 'cancelled') throw new Error('workflow cancelled')
if (workflow.state === 'failed') throw new Error('workflow failed')
await sleep(pollInterval)
}

404 response:

A 404 means the workflow ID is unknown — either it never existed, or it completed and expired from workflow history. If you submitted the workflow successfully and received a workflow_id, treat 404 as an unexpected condition: log an error, wait a backoff period, and resubmit the operation. Do not treat 404 as a normal polling outcome.

The appropriate backoff interval depends on your deployment topology — localhost, reverse proxy, or Tor all have meaningfully different latency characteristics.

Alternatively, subscribe to workflow.progress events to receive completion notification by push rather than polling.


Cancels a pending or running workflow.

DELETE /workflows/WTjV1t4Gdk

Response:

{"result": "Workflow WTjV1t4Gdk queued for deletion"}

Behaviour by state:

StateResult
pendingRemoved from the queue — transitions to cancelled
runningCancelled — in-progress step is abandoned, transitions to cancelled
completedNo-op — workflow has already completed
cancelledNo-op — workflow already cancelled
failedNo-op — workflow already failed

Cancelled workflows are added to history immediately and remain visible for up to history_ttl seconds. A caller polling GET /workflows/{id} after a successful DELETE will receive state: cancelled for the duration of the retention window, not a 404.

404 response:

The workflow ID is unknown or has expired from workflow history.


Submit a new workflow. Returns immediately with a workflow ID.

POST /workflows

Request body:

{
"workflow": {
"name": "Import wallet-1",
"request_id": "256483",
"args": {
"pubkeys_set": "test/wallet-1"
},
"steps": [
{
"step": "pubkeys.extpubkey.import",
"args": {
"extpubkey": "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs",
"scanspec": {"from": 0, "gap_limit": 20}
}
},
{"step": "pubkeys.scripthash.subscribe"},
{"step": "pubkeys.scripthash.history"},
{"step": "pubkeys.transaction.fetch"},
{"step": "blockheaders.unresolved.fetch"},
{"step": "pubkeys.unspent.compute"}
]
}
}

Response:

{
"workflow_id": "WTjV1t4GNc",
"request_id": "256483"
}

request_id is omitted from the response if not provided in the request.

Workflow fields:

FieldRequiredDescription
namenoHuman-readable label
request_idnoCaller-assigned ID, echoed in response and progress events. 1–127 characters
dry_runnoIf true, validate the workflow without submitting it
args.pubkeys_setyes (pubkeys workflows)Name of the pubkeys set
stepsyesArray of step objects

Dry run:

Set dry_run: true to validate a workflow submission without executing it. Useful for debugging workflow syntax before running the workflow. On success:

{
"workflow_id": "SUCCESS",
"request_id": "256483"
}

Validation errors are returned in the standard error envelope. See Error Responses.


Steps are the individual units of work in a workflow. Each step is a pre-defined operation — importing addresses, fetching transactions, computing UTXOs — and a workflow is simply an ordered list of steps composed to achieve a goal. Steps execute in sequence; include only what your use case requires.

Pubkeys workflow steps:

StepDescription
pubkeys.set.create ¹Create pubkeys set
pubkeys.set.deleteClear cached records for the pubkeys set
pubkeys.extpubkey.importImport derived addresses into pubkeys_set ²
pubkeys.pubkey.importImport discrete addresses into pubkeys_set ²
pubkeys.scripthash.subscribeSubscribe addresses to the indexer
pubkeys.scripthash.unsubscribeUnsubscribe addresses from the indexer
pubkeys.scripthash.historyRetrieve transaction history from the indexer
pubkeys.transaction.fetchFetch full transaction data
pubkeys.fee.prepDiscover previous out transactions for computing transaction fee
transactions.unresolved.fetchResolve required transactions (previous out, etc)
blockheaders.unresolved.fetchResolve block headers for fetched transactions
pubkeys.unspent.computeCompute the UTXO set

¹ Pubkeys sets are automatically created if they don’t already exist — pubkeys.set.create is rarely needed directly.

² Derived and discrete addresses cannot be mixed in the same pubkeys_set. A pubkeys_set is either xpub-derived or discretely composed. If you need both, use separate pubkeys_sets and query them independently. See Wallets & ScriptPubKeys.


Workflows have two levels of arguments. Workflow-level args are global — shared context available to every step. For pubkeys workflows, pubkeys_set is the primary workflow-level arg, telling all steps which wallet they’re operating on. Step-level args are specific to a single step and supply additional inputs that step alone requires. Steps without their own args block rely entirely on the workflow-level args.

{
"workflow": {
"args": {"pubkeys_set": "test/wallet-1"},
"steps": [
{
"step": "pubkeys.extpubkey.import",
"args": {
"extpubkey": "zpub6rFR7y4Q2AijB...",
"scanspec": {"from": 0, "gap_limit": 20}
}
},
{"step": "pubkeys.scripthash.subscribe"},
{"step": "pubkeys.scripthash.history"}
]
}
}

Here pubkeys_set is the workflow-level arg — every step knows which wallet it’s working with. extpubkey and scanspec are step-level args — only needed by pubkeys.extpubkey.import. Steps like pubkeys.scripthash.subscribe and pubkeys.scripthash.history have no step-level args of their own.

The following steps accept step-level args:

Accepts an extended public key and three scanspec forms: gap_limit, count and range. See Wallets & ScriptPubKeys for details.

FieldRequiredDescription
extpubkeyyesxpub, ypub, or zpub string
scanspecyesAddress derivation specification

Accepts plain addresses, raw public keys with explicit script type prefix, or both combined in a single step.

Plain addresses:

{
"step": "pubkeys.pubkey.import",
"args": {
"addresses": [
"bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu",
"bc1qp7lsyxsf0p9049dgea2t0cynx6d3ama0xe3pdv"
]
}
}

Raw public keys with explicit script type prefix:

{
"step": "pubkeys.pubkey.import",
"args": {
"pubkeys": [
"p2wpkh:02d8e638a311b13f6a097b33fead3fe9b02b1e04c9a28a87e80b3598fc7a2c4d90",
"p2pkh:02d8e638a311b13f6a097b33fead3fe9b02b1e04c9a28a87e80b3598fc7a2c4d90",
"p2sh-p2wpkh:02d8e638a311b13f6a097b33fead3fe9b02b1e04c9a28a87e80b3598fc7a2c4d90",
"p2pk:04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"
]
}
}

p2sh-p2wpkh and p2shwpkh are both accepted as the prefix for wrapped SegWit keys.

Combined in a single step:

{
"step": "pubkeys.pubkey.import",
"args": {
"addresses": [
"bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"
],
"pubkeys": [
"p2pk:04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"
]
}
}

Returns results of a core.* workflow. Each item corresponds to a step in the submitted workflow, in order. Results are passed through verbatim from bitcoind. Refer to the bitcoind RPC documentation for result shapes, error codes, and method details.

GET /workflows/WTjV1t4Gdk/results/core

Response:

[
{
"step": 1,
"method": "getblockcount",
"args": [],
"result": 292427,
"error": null
},
{
"step": 2,
"method": "getmempoolentry",
"args": ["82cb1fc54a4d891d687c3898a7ebcd54da996e7f26e1fd6f2a21dc2f655194fa"],
"result": null,
"error": {
"code": -5,
"message": "Transaction not in mempool"
}
}
]

Steps with RPC errors return the bitcoind error code and message. The workflow continues executing remaining steps regardless of individual step errors.

Submitting a core workflow:

Use the core. prefix followed by the bitcoind RPC method name. Args are passed as a JSON array in the same order as the bitcoind RPC method signature. In general, do not mix core.* steps with pubkeys workflow steps in the same workflow.

{
"workflow": {
"steps": [
{"step": "core.getblockcount"},
{"step": "core.getblockchaininfo"},
{"step": "core.estimatesmartfee", "args": [6, "ECONOMICAL"]},
{"step": "core.getbestblockhash"},
{"step": "core.getblockhash", "args": [292411]},
{"step": "core.validateaddress", "args": ["tb1q8768jg36dw0zqwkjt4wp8pp75uc2zkjhp5q9qr"]},
{"step": "core.gettxout", "args": ["<txid>", 0]},
{"step": "core.getmempoolentry", "args": ["<txid>"]},
{"step": "core.getchaintips"},
{"step": "core.getdifficulty"},
{"step": "core.getconnectioncount"},
{"step": "core.getnettotals"},
{"step": "core.uptime"}
]
}
}

Retrieve cached transaction and UTXO data for a pubkeys set workflow. Results are only available after the workflow that populated them has completed. There are two ways to know when a workflow is done:

Polling — call GET /workflows/{id} periodically until state is completed.

WebSocket — subscribe to workflow.progress and listen for the event where state is completed. See the WebSocket reference for the event schema.

Once complete, results are cached and can be queried as many times as needed without re-running the workflow. See Workflow Model for the full lifecycle.

See ScriptPubKeys Set for naming conventions and security considerations.


To retrieve transactions for a pubkeys set once results are available:

POST /pubkeys-set/txns

Request body:

{"pubkeys_set": "test/wallet-1"}

Request fields:

FieldRequiredDescription
pubkeys_setyesName of the pubkeys set
limitnoResults per page. Set to 0 to return all records. Default 100
offsetnoPagination offset, default 0
from_blocknoFilter to transactions at or above this height
to_blocknoFilter to transactions at or below this height
exclude_unconfirmednoExclude mempool transactions, default false
include_feenoInclude fee-related fields in the response, default false. See note below.
sort_ordernoasc or desc, default desc

Response:

{
"txns": [
{
"timestamp": 1773359301,
"datetime": "2026-03-12 23:48:21",
"size": 222,
"inputs": 1,
"ins_total": 217572,
"fee": 1473,
"weight": 561,
"outputs": 2,
"outs_total": 216099,
"txid": "72ab93c9e66f3cad39620f4689748704012b9f28acf896b348208e0fc0bafa73",
"own_ins": [
{
"txidx": 0,
"prev_txid": "806cffbbd657119e03f12b5039794e180456f96b07b689bb162a5a7787ea9f2e",
"prev_txidx": 1,
"hdidx": "1:8",
"address": "tb1qk9jg6xwkx7s8vz2mpj5w2jayqdk9lks6y00j0k",
"value": 217572
}
],
"own_outs": [
{
"hdidx": "1:9",
"address": "tb1qne62gqgn4kx3l0eh4c3yhjhjkmjhfv5j63zq9h",
"txidx": 1,
"value": 206980,
"spent": false
}
],
"height": 295322
}
],
"pagination": {
"total": 11,
"returned": 1,
"limit": 1,
"offset": 0
}
}

Confirmation status:

heightdatetimeMeaning
> 0block timestampConfirmed at this block height
0absentUnconfirmed — all inputs confirmed
-1absentUnconfirmed — at least one input unconfirmed

Response fields:

FieldDescription
txidTransaction ID
heightBlock height. See confirmation status table above
timestampBlock timestamp as Unix epoch. Absent for unconfirmed transactions
datetimeBlock timestamp as UTC string. Absent for unconfirmed transactions
sizeTransaction size in bytes
weightTransaction weight units
inputsTotal number of inputs
ins_totalTotal value of all inputs in satoshis. Absent for coinbase transactions. Otherwise only present when include_fee is true and all input prev-outs were resolved
feeMiner fee in satoshis. Absent for coinbase transactions. Otherwise only present when include_fee is true and all input prev-outs were resolved. Use with weight to compute fee rate — see note above
outputsTotal number of outputs
outs_totalTotal value of all outputs in satoshis

Each transaction may have many inputs and outputs, but own_ins and own_outs contain only those belonging to addresses in the pubkeys set.

Own inputs (own_ins):

FieldDescription
own_ins[].txidxIndex of this input within the transaction
own_ins[].prev_txidTXID of the output being spent
own_ins[].prev_txidxOutput index within the previous transaction
own_ins[].hdidxHD derivation path in account:index format. Omitted for discretely imported addresses
own_ins[].addressAddress for this input
own_ins[].valueValue in satoshis

Own outputs (own_outs):

FieldDescription
own_outs[].txidxIndex of this output within the transaction
own_outs[].hdidxHD derivation path in account:index format. Omitted for discretely imported addresses
own_outs[].addressAddress for this output
own_outs[].valueValue in satoshis
own_outs[].spentWhether this output has been spent

Pagination:

Results can be paginated to page through large sets. Use limit and offset to retrieve pages, or set limit: 0 to return all records. Transactions can be filtered to a block height range using from_block and to_block. Results can be sorted by height, ascending or descending. Unconfirmed transactions are included by default; set exclude_unconfirmed: true to exclude them.

The pagination object describes the current page and the full result set, so you can calculate total pages and navigate forward.

FieldDescription
pagination.totalTotal number of transactions matching the query
pagination.returnedNumber of transactions in this response
pagination.limitPage size used for this query
pagination.offsetOffset used for this query

All values are in satoshis.


To retrieve UTXOs for a pubkeys set once results are available:

POST /pubkeys-set/utxos

Request body:

{"pubkeys_set": "test/wallet-1"}

Request fields:

FieldRequiredDescription
pubkeys_setyesName of the pubkeys set
limitnoResults per page. Set to 0 to return all records. Default 100
offsetnoPagination offset, default 0
min_amountnoMinimum value in satoshis
max_amountnoMaximum value in satoshis
sort_bynoheight or value, default height
sort_ordernoasc or desc, default desc

Response:

{
"utxos": [
{
"timestamp": 1771355949,
"datetime": "2026-02-17 19:19:09",
"value": 8505,
"txid": "806cffbbd657119e03f12b5039794e180456f96b07b689bb162a5a7787ea9f2e",
"txidx": 0,
"height": 291998,
"address": "tb1qe02jn6rsq20637hacsphxqxrdpnnmcqt9eh4q7",
"hdidx": "0:8"
}
],
"pagination": {
"total": 1,
"returned": 1,
"limit": 100,
"offset": 0
},
"summary": {
"total_count": 1,
"total_amount": 8505
}
}

Response fields:

FieldDescription
txidTransaction ID
heightBlock height. See confirmation status table above
timestampBlock timestamp as Unix seconds
datetimeBlock timestamp as UTC string
txidxIndex of this output within the transaction
addressAddress for this output
hdidxHD derivation path in account:index format. Omitted for discretely imported addresses
valueUTXO value in satoshis

Pagination:

Results can be paginated to page through large sets. Use limit and offset to retrieve pages, or set limit: 0 to return all records. UTXOs can be filtered by value using min_amount and max_amount. Results can be sorted by height or value, ascending or descending.

See POST /pubkeys-set/txns for full pagination details — behaviour is identical.

FieldDescription
pagination.totalTotal number of UTXOs matching the query
pagination.returnedNumber of UTXOs in this response
pagination.limitPage size used for this query
pagination.offsetOffset used for this query

Summary:

summary reflects the full result set, not just the current page. Use it to display total balance and UTXO count without paginating through all results.

FieldDescription
summary.total_countTotal number of UTXOs matching the query
summary.total_amountTotal value of all matching UTXOs in satoshis