Api

Client API

On-chain transaction creation, signing, and broadcasting

The Client API exposes Hydra App's ClientService — on-chain transaction creation, signing, and broadcasting for native assets, tokens, fee bumps, and token allowances.

JSON-RPC namespace: client

The service exposes two parallel flows:

  • Build → sign → finalizeCreate*Transaction returns an unsigned TransactionRequest. Sign it with signer.SignTransaction, then submit with FinalizeAndBroadcastTransaction. Use this when you want to inspect the transaction or use an external signer.
  • One-shot sendSendTransaction, SendTokenTransaction, BumpTransaction, SetTokenAllowance build, sign, and broadcast in a single call.

Looking for GetDepositAddress? That moved to the Wallet API.

Endpoints

Build → sign → finalize

One-shot

Token permits (2026-09-11)


Create Send Transaction

Builds an unsigned native asset send transaction. Returns a generic TransactionRequest that can be inspected, signed via signer.SignTransaction, and then submitted via FinalizeAndBroadcastTransaction.

Method: CreateSendTransaction

Parameters:

NameTypeRequiredDescription
networkNetworkYESTarget network
tostringYESRecipient address
amountAmountYESAmount to send (exact or all-available)
fee_optionFeeOptionYESFee selection

Response:

FieldTypeDescription
transaction_requestTransactionRequestUnsigned transaction request

Example Request:

import { CreateSendTransactionRequest } from './proto/client_pb'

const request = new CreateSendTransactionRequest()
request.setNetwork({ protocol: 1, id: '0a03cf40' })
request.setTo('bc1qrecipient...')
request.setAmount({ exact: { amount: { value: '0.01' } } })
request.setFeeOption({ /* see Fee API for options */ })

const response = await client.createSendTransaction(request, {})
const txRequest = response.getTransactionRequest()
// → pass txRequest to signer.SignTransaction

Create Bump Transaction

Builds an unsigned fee-bump (RBF / speed-up) transaction request for an existing unconfirmed transaction.

Method: CreateBumpTransaction

Parameters:

NameTypeRequiredDescription
networkNetworkYESTarget network
txidstringYESTransaction ID to replace
fee_optionFeeOptionYESNew (higher) fee option

Response:

FieldTypeDescription
transaction_requestTransactionRequestUnsigned bump transaction

Example Request:

import { CreateBumpTransactionRequest } from './proto/client_pb'

const request = new CreateBumpTransactionRequest()
request.setNetwork({ protocol: 1, id: '0a03cf40' })
request.setTxid('abc123...')
request.setFeeOption({ /* higher fee */ })

const response = await client.createBumpTransaction(request, {})
const txRequest = response.getTransactionRequest()

Create Token Send Transaction

Builds an unsigned ERC-20 / token transfer transaction. For native asset transfers use Create Send Transaction.

Method: CreateTokenSendTransaction

Parameters:

NameTypeRequiredDescription
networkNetworkYESTarget network (must support tokens, e.g. EVM)
tostringYESRecipient address
token_idstringYESToken contract address / identifier
amountAmountYESAmount of tokens
fee_optionFeeOptionYESFee selection

Response:

FieldTypeDescription
transaction_requestTransactionRequestUnsigned token transfer transaction

Example Request:

import { CreateTokenSendTransactionRequest } from './proto/client_pb'

const request = new CreateTokenSendTransactionRequest()
request.setNetwork({ protocol: 2, id: '11155111' })
request.setTo('0xrecipient...')
request.setTokenId('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48') // USDC
request.setAmount({ exact: { amount: { value: '100' } } })
request.setFeeOption({ /* ... */ })

const response = await client.createTokenSendTransaction(request, {})
const txRequest = response.getTransactionRequest()

Create Set Token Allowance Transaction

Builds an unsigned token allowance approval transaction. Supports ERC-20, ERC-721, and ERC-1155.

Method: CreateSetTokenAllowanceTransaction

Parameters:

NameTypeRequiredDescription
networkNetworkYESTarget network (must support tokens, e.g. EVM)
spenderstringYESSpender address to approve
allowanceSetTokenAllowanceYESToken type, amount, and approval status
fee_optionFeeOptionYESFee selection

Response:

FieldTypeDescription
transaction_requestTransactionRequestUnsigned allowance approval transaction

Example Request:

import { CreateSetTokenAllowanceTransactionRequest } from './proto/client_pb'

const request = new CreateSetTokenAllowanceTransactionRequest()
request.setNetwork({ protocol: 2, id: '11155111' })
request.setSpender('0xspender...')
request.setAllowance({ /* SetTokenAllowance */ })
request.setFeeOption({ /* ... */ })

const response = await client.createSetTokenAllowanceTransaction(request, {})
const txRequest = response.getTransactionRequest()

Finalize And Broadcast Transaction

Submits a signed transaction to the network. Accepts a SignedTransactionRequest (as returned by signer.SignTransaction) and returns the resulting transaction ID.

Method: FinalizeAndBroadcastTransaction

Parameters:

NameTypeRequiredDescription
networkNetworkYESTarget network
signed_txSignedTransactionRequestYESSigned transaction to finalize and broadcast

Response:

FieldTypeDescription
txidstringBroadcast transaction ID

Example Request:

import { FinalizeAndBroadcastTransactionRequest } from './proto/client_pb'

// signedTx is the SignedTransactionRequest returned by signer.SignTransaction
const request = new FinalizeAndBroadcastTransactionRequest()
request.setNetwork({ protocol: 1, id: '0a03cf40' })
request.setSignedTx(signedTx)

const response = await client.finalizeAndBroadcastTransaction(request, {})
console.log('Broadcast TXID:', response.getTxid())

Example Response:

{ "txid": "abc123def456..." }

Send Transaction

Send the native asset of a network to another address. Builds, signs and broadcasts in one call.

Method: SendTransaction

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork to send on
tostringYESRecipient address (or a Bitcoin script)
amountAmountYESAmount to send
fee_optionFeeOptionYESFee selection

Amount

Amount is a oneof, not a struct with a value. Exactly one arm is set, and it carries no asset id — the asset is the network's native one for SendTransaction, and token_id for SendTokenTransaction.

ArmPayloadMeaning
all(empty)Send the entire available balance
exact{ amount: DecimalString }Send exactly this much, in whole units

FeeOption

FeeOption is also a oneof message, not an enum. Three arms select a network estimate; the fourth carries an explicit rate.

ArmPayloadMeaning
low(empty)The network's low-priority estimate
medium(empty)The standard estimate
high(empty)The fastest estimate
custom{ fee_rate: FeeRate }Your own max_fee_per_unit / priority_fee_per_unit

There is no FeeOption.MEDIUM constant. Older revisions of this page showed FeeOption as an enum with LOW / MEDIUM / HIGH / CUSTOM values and Amount as { value, asset_id }. Neither has ever existed in the proto — see Fee structures for the real shapes.

Response:

FieldTypeDescription
txidstringBroadcast transaction ID

Example Request:

import { SendTransactionRequest } from './proto/client_pb'

const request = new SendTransactionRequest()
request.setNetwork({ protocol: 1, id: '0a03cf40' })
request.setTo('tb1q...')
request.setAmount({ exact: { amount: { value: '0.001' } } })
request.setFeeOption({ medium: {} })

const response = await client.sendTransaction(request, {})
console.log('Transaction ID:', response.getTxid())

Example Response:

{ "txid": "abc123..." }

Send Token Transaction

Send a token (non-native asset) to another address. For native transfers use Send Transaction.

Method: SendTokenTransaction

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork to send on (must support tokens — EVM or Tron)
tostringYESRecipient address
token_idstringYESToken identifier, e.g. erc20:0x…
amountAmountYESAmount to send, in the token's whole units
fee_optionFeeOptionYESFee selection

Response:

FieldTypeDescription
txidstringBroadcast transaction ID

Example Request:

// Send 100 USDC on Ethereum Sepolia
const request = new SendTokenTransactionRequest()
request.setNetwork({ protocol: 2, id: '11155111' })
request.setTo('0xrecipient...')
request.setTokenId('erc20:0x8cd0da3d001b013336918b8bc4e56d9dda1347e0')
request.setAmount({ exact: { amount: { value: '100' } } })
request.setFeeOption({ medium: {} })

const response = await client.sendTokenTransaction(request, {})

Example Response:

{ "txid": "0x..." }

The token must be registered on the node — listed under the network's tokens in config.yaml, or added at runtime with asset.AddToken. An unregistered token_id is rejected.


Bump Transaction

Increase the fee of an existing unconfirmed transaction to speed up confirmation (RBF on Bitcoin, a same-nonce replacement on EVM).

Method: BumpTransaction

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork where the transaction exists
txidstringYESTransaction ID to replace
fee_optionFeeOptionYESNew fee, which must price above the original

Response:

FieldTypeDescription
txidstringThe replacement transaction's ID

Example Request:

const request = new BumpTransactionRequest()
request.setNetwork({ protocol: 1, id: '0a03cf40' })
request.setTxid('abc123...')
request.setFeeOption({ high: {} })

const response = await client.bumpTransaction(request, {})

Example Response:

{ "txid": "def456..." }

Set Token Allowance

Approve a spender to move tokens on the wallet's behalf. Builds, signs and broadcasts in one call.

Method: SetTokenAllowance

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork (must support tokens — EVM or Tron)
spenderstringYESAddress being approved
allowanceSetTokenAllowanceYESWhich approval to set — see below
fee_optionFeeOptionYESFee selection

SetTokenAllowance

A oneof over the three approval shapes the token standards use:

ArmPayloadStandard
token{ token_id: string, amount: AllowanceAmount }ERC-20 / TRC-20 approve
unique_token{ token_id: string, approved: bool }ERC-721 approve
contract{ contract_address: string, token_variant: string, approved: bool }ERC-1155 setApprovalForAll

AllowanceAmount is itself a oneof: unlimited (empty) or exact { amount: DecimalString }.

Response:

FieldTypeDescription
txidstringBroadcast transaction ID

Example Request:

const request = new SetTokenAllowanceRequest()
request.setNetwork({ protocol: 2, id: '11155111' })
request.setSpender('0xspender...')
request.setAllowance({
  token: {
    token_id: 'erc20:0x8cd0da3d001b013336918b8bc4e56d9dda1347e0',
    amount: { exact: { amount: { value: '1000' } } },
  },
})
request.setFeeOption({ medium: {} })

const response = await client.setTokenAllowance(request, {})

Example Response:

{ "txid": "0x..." }

Read the current state back with blockchain.GetTokenAllowance.


Token permits

Added 2026-09-11.

A token permit is an allowance its owner authorises with an off-chain signature instead of an on-chain approve. Whoever submits the permit pays the gas — so a wallet that holds a token but no native asset can still grant an allowance, as long as someone else sends the transaction.

The three RPCs below are the submitting side: this wallet sends a permit that some owner signed, and pays for it. owner and this wallet are usually different parties.

The flow:

  1. Read the token's terms with blockchain.GetTokenPermitTerms. No terms ⇒ the token offers no signature-authorised allowance to that owner, and the permit path is unavailable — fall back to SetTokenAllowance.
  2. Price the submission with EstimateTokenPermitFee. The permit does not exist yet — the owner signs once the price is known.
  3. The owner signs the terms, producing a TokenPermit.
  4. Submit it with SubmitTokenPermit, or build it unsigned with CreateTokenPermitTransaction for an external signer.

TokenPermit

FieldTypeDescription
token_idstringThe token the permit is for
ownerstringThe address granting the allowance and signing the permit
spenderstringThe address the allowance is granted to
valueU256StringThe allowance, in the token's smallest unit
deadlineuint64Unix seconds after which the authorisation lapses
termsbytesThe terms the token disclosed when the owner signed, serialised by the network's protocol — the same bytes GetTokenPermitTerms returns
signaturebytesThe owner's raw signature bytes

⚠️ TokenPermit.value is the one amount that is not in whole units

It is a U256String in the token's smallest unit — the value that goes on the wire to the contract — because that is what the owner's signature commits to, and re-deriving it from a decimal would risk signing for a different number. Everywhere else in this API an amount is a whole-unit DecimalString; EstimateTokenPermitFee.value below is one of those.


Estimate Token Permit Fee

What submitting a permit would cost this wallet, priced against the owner's live allowance and permit nonce on the token. Nothing is signed or sent.

Method: EstimateTokenPermitFee

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork (must support tokens, e.g. EVM)
asset_idstringYESThe token the permit is for
ownerstringYESThe wallet whose permit would be submitted
spenderstringYESThe address the permit would allow to spend
valueDecimalStringYESThe allowance the permit would grant, in whole units, rounded up to the token's precision
fee_optionFeeOptionYESThe fee option to price the submission at

Response:

FieldTypeDescription
feeDecimalStringWhat the submission would cost, in the network's native asset

Call this before the owner signs. Pricing needs the owner's live state on the token — an allowance already at the target value, or a different permit nonce, changes what the submission costs — which is exactly why it takes no signature.


Create Token Permit Transaction

Builds the unsigned transaction that submits a signed permit, for inspection or an external signer. The permit's owner authorised the allowance off-chain; this wallet sends the transaction and pays the fee.

Method: CreateTokenPermitTransaction

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork (must support tokens, e.g. EVM)
permitTokenPermitYESThe permit to submit
fee_optionFeeOptionYESFee for the submitting transaction

Response:

FieldTypeDescription
transaction_requestTransactionRequestUnsigned transaction — sign it, then FinalizeAndBroadcastTransaction

Submit Token Permit

Creates, signs and broadcasts the transaction that sets the allowance the permit's owner authorised.

Method: SubmitTokenPermit

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork (must support tokens, e.g. EVM)
permitTokenPermitYESThe permit to submit
fee_optionFeeOptionYESFee for the submitting transaction

Response:

FieldTypeDescription
txidstringBroadcast transaction ID

Example Request:

import { SubmitTokenPermitRequest } from './proto/client_pb'

const request = new SubmitTokenPermitRequest()
request.setNetwork({ protocol: 2, id: '11155111' })
request.setPermit({
  token_id: 'erc20:0x8cd0da3d001b013336918b8bc4e56d9dda1347e0',
  owner: '0xowner...',
  spender: '0xspender...',
  value: { value: '1000000000' },          // smallest unit
  deadline: Math.floor(Date.now() / 1000) + 3600,
  terms: termsBytes,                        // from blockchain.GetTokenPermitTerms
  signature: signatureBytes,                // the owner's signature over those terms
})
request.setFeeOption({ medium: {} })

const response = await client.submitTokenPermit(request, {})
console.log('permit submitted:', response.getTxid())

Example Response:

{ "txid": "0x..." }

A permit whose deadline has passed, whose signature does not match owner, or whose nonce the token has already consumed is rejected by the token contract — the transaction reverts and the fee is still spent. Submit promptly after signing, and re-read the terms if a submission fails.

The same rail is what the liquidity service uses to fund a channel for a wallet that holds the token but no gas — see SponsoredDepositFeePayment and the swap DepositRail in Swap API.


Network ID Formats

Different networks use different ID formats:

Bitcoin Networks

  • Mainnet: f9beb4d9
  • Testnet4: 1c163f28
  • Testnet3: 0b110907
  • Signet: 0a03cf40
  • Regtest: fabfb5da

EVM Networks

  • Ethereum Mainnet: 1
  • Ethereum Sepolia: 11155111
  • Arbitrum One: 42161
  • Arbitrum Sepolia: 421614
  • Optimism: 10
  • Polygon: 137

Common Patterns

Build → sign → broadcast with an external signer

// 1. Build the unsigned transaction.
const build = new CreateSendTransactionRequest()
build.setNetwork({ protocol: 1, id: '0a03cf40' })
build.setTo('tb1q...')
build.setAmount({ exact: { amount: { value: '0.001' } } })
build.setFeeOption({ medium: {} })

const txRequest = (await client.createSendTransaction(build, {})).getTransactionRequest()

// 2. `signable_data` is the wallet-standard payload for the network:
//    EVM → UTF-8 JSON eth_sendTransaction params; Bitcoin → base64 BIP-174 PSBT.
//    Hand it to signer.SignTransaction, or to your own signer.
const signed = await signerClient.signTransaction(/* … txRequest … */)

// 3. Finalize + broadcast.
const fin = new FinalizeAndBroadcastTransactionRequest()
fin.setNetwork({ protocol: 1, id: '0a03cf40' })
fin.setSignedTx(signed)

console.log('txid:', (await client.finalizeAndBroadcastTransaction(fin, {})).getTxid())

TransactionRequest carries the estimate toofee_units (gas for EVM, weight units for Bitcoin) and an optional fee. Check them before signing; the one-shot Send* calls give you no such window.

Deposit addresses

Deposit addresses are on the Wallet API, not here — wallet.GetDepositAddress for the wallet's standing address, wallet.GetUniqueDepositAddress for a fresh one per invoice on Bitcoin.


Error Handling

Error CodeDescriptionSolution
INVALID_ARGUMENTInvalid network, address, or amountVerify all parameters are correctly formatted
FAILED_PRECONDITIONInsufficient balance or wallet not initializedCheck balance and wallet status
NOT_FOUNDTransaction not found (for BumpTransaction)Verify transaction exists and is unconfirmed
UNAVAILABLEService temporarily unavailableRetry with exponential backoff

Best Practices

  1. Validate addresses before sending. An on-chain send is irreversible; the API does not second-guess a well-formed address.
  2. Check the balance firstwallet.GetBalance. The native asset also has to cover the fee, which is a separate resource from the token you are moving.
  3. Pick the fee by urgencylow when you can wait, high when you cannot, custom when you have your own estimate.
  4. Prefer build → sign → finalize when the amount is large. It is the only flow that lets you inspect fee_units and fee before committing.
  5. Follow the transaction afterwardswallet.GetTransaction, or the TransactionUpdate on SubscribeClientEvents.
  6. Bump rather than resend. A second send is a second transaction; BumpTransaction replaces the first.

← Back to API Reference | Next: Asset API →


Copyright © 2025