Api

Node API

Channel and payment management

The Node API provides Lightning Network channel management and payment operations.

Endpoints

Peer Management

Channel Operations

Payment Operations

Invoice Management


Connect to Peer

Connect to a Lightning Network peer.

Method: ConnectToPeer

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork to connect on
peer_urlstringYESPeer connection URL

Peer URL Format:

node_id@host:port

Response: Empty (success confirmation)

Example Request:

TypeScript
import { NodeServiceClient } from './proto/NodeServiceClientPb'
import { ConnectToPeerRequest } from './proto/node_pb'

const client = new NodeServiceClient('http://localhost:5001')

const request = new ConnectToPeerRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setPeerUrl('02abc123...@lightning.example.com:9735')

await client.connectToPeer(request, {})
console.log('Connected to peer')
Go
import (
    pb "github.com/hydra/hydra-go/proto"
)

client := pb.NewNodeServiceClient(conn)

req := &pb.ConnectToPeerRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    PeerUrl: "02abc123...@lightning.example.com:9735",
}

_, err := client.ConnectToPeer(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Connected to peer")
Rust
use hydra_api::node_service_client::NodeServiceClient;
use hydra_api::{ConnectToPeerRequest, Network};

let mut client = NodeServiceClient::new(channel);

let request = tonic::Request::new(ConnectToPeerRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    peer_url: "02abc123...@lightning.example.com:9735".to_string(),
});

client.connect_to_peer(request).await?;
println!("Connected to peer");

Get Connected Peers

Get list of currently connected peers.

Method: GetConnectedPeers

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork to query

Response:

FieldTypeDescription
node_idsstring[]Array of connected node IDs

Example Request:

TypeScript
const request = new GetConnectedPeersRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})

const response = await client.getConnectedPeers(request, {})
const peers = response.getNodeIdsList()
console.log('Connected peers:', peers)
Go
req := &pb.GetConnectedPeersRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
}

resp, err := client.GetConnectedPeers(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

peers := resp.NodeIds
log.Println("Connected peers:", peers)
Rust
let request = tonic::Request::new(GetConnectedPeersRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
});

let response = client.get_connected_peers(request).await?;
let peers = response.into_inner().node_ids;
println!("Connected peers: {:?}", peers);

Estimate Open Channel Fee

Estimate the onchain fee for opening a new channel.

Method: EstimateOpenChannelFee

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork to open channel on
node_idstringYESPeer's node ID
asset_amountsmap<string, SendAmount>YESMap of asset_id → amount to allocate
fee_rateFeeRateYESTransaction fee rate

SendAmount Object (one of):

FieldTypeDescription
valueDecimalStringExact amount
max-Use maximum available

FeeRate Object (one of):

FieldTypeDescription
absoluteDecimalStringAbsolute fee amount
relativeDecimalStringRelative fee (e.g., sat/vB)

Response:

FieldTypeDescription
feeDecimalStringEstimated onchain fee

Example Request:

TypeScript
const request = new EstimateOpenChannelFeeRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setNodeId('02abc123...')
request.setAssetAmountsMap({
  'BTC': { value: '10000000' } // 0.1 BTC
})
request.setFeeRate({ relative: '5' }) // 5 sat/vB

const response = await client.estimateOpenChannelFee(request, {})
console.log('Estimated fee:', response.getFee(), 'sats')
Go
req := &pb.EstimateOpenChannelFeeRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    NodeId: "02abc123...",
    AssetAmounts: map[string]*pb.SendAmount{
        "BTC": {Value: "10000000"}, // 0.1 BTC
    },
    FeeRate: &pb.FeeRate{Relative: "5"}, // 5 sat/vB
}

resp, err := client.EstimateOpenChannelFee(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Estimated fee:", resp.Fee, "sats")
Rust
let mut asset_amounts = HashMap::new();
asset_amounts.insert("BTC".to_string(), SendAmount {
    value: "10000000".to_string(), // 0.1 BTC
});

let request = tonic::Request::new(EstimateOpenChannelFeeRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    node_id: "02abc123...".to_string(),
    asset_amounts,
    fee_rate: Some(FeeRate {
        relative: "5".to_string(), // 5 sat/vB
    }),
});

let response = client.estimate_open_channel_fee(request).await?;
println!("Estimated fee: {} sats", response.into_inner().fee);

Open Channel

Open a new Lightning channel with a peer.

Method: OpenChannel

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork to open on
node_idstringYESPeer's node ID
asset_amountsmap<string, SendAmount>YESAssets to allocate
fee_rateFeeRateYESTransaction fee rate

Response:

FieldTypeDescription
txidstringFunding transaction ID
channel_idstringChannel identifier

Example Request:

TypeScript
const request = new OpenChannelRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setNodeId('02abc123...')
request.setAssetAmountsMap({
  'BTC': { value: '10000000' }
})
request.setFeeRate({ relative: '5' })

const response = await client.openChannel(request, {})
console.log('Channel opened!')
console.log('  TX:', response.getTxid())
console.log('  Channel ID:', response.getChannelId())
Go
req := &pb.OpenChannelRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    NodeId: "02abc123...",
    AssetAmounts: map[string]*pb.SendAmount{
        "BTC": {Value: "10000000"},
    },
    FeeRate: &pb.FeeRate{Relative: "5"},
}

resp, err := client.OpenChannel(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Channel opened!")
log.Println("  TX:", resp.Txid)
log.Println("  Channel ID:", resp.ChannelId)
Rust
let mut asset_amounts = HashMap::new();
asset_amounts.insert("BTC".to_string(), SendAmount {
    value: "10000000".to_string(),
});

let request = tonic::Request::new(OpenChannelRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    node_id: "02abc123...".to_string(),
    asset_amounts,
    fee_rate: Some(FeeRate {
        relative: "5".to_string(),
    }),
});

let response = client.open_channel(request).await?;
let inner = response.into_inner();
println!("Channel opened!");
println!("  TX: {}", inner.txid);
println!("  Channel ID: {}", inner.channel_id);

Estimate Deposit Channel Fee

Estimate fee for depositing assets into an existing channel.

Method: EstimateDepositChannelFee

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
channel_idstringYESChannel to deposit to
asset_amountsmap<string, SendAmount>YESAssets to deposit
fee_rateFeeRateYESTransaction fee rate

Response:

FieldTypeDescription
feeDecimalStringEstimated fee

Example Request:

TypeScript
const request = new EstimateDepositChannelFeeRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setChannelId('ch_abc123')
request.setAssetAmountsMap({
  'BTC': { value: '5000000' } // Add 0.05 BTC
})
request.setFeeRate({ relative: '5' })

const response = await client.estimateDepositChannelFee(request, {})
console.log('Deposit fee:', response.getFee())
Go
req := &pb.EstimateDepositChannelFeeRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    ChannelId: "ch_abc123",
    AssetAmounts: map[string]*pb.SendAmount{
        "BTC": {Value: "5000000"}, // Add 0.05 BTC
    },
    FeeRate: &pb.FeeRate{Relative: "5"},
}

resp, err := client.EstimateDepositChannelFee(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Deposit fee:", resp.Fee)
Rust
let mut asset_amounts = HashMap::new();
asset_amounts.insert("BTC".to_string(), SendAmount {
    value: "5000000".to_string(), // Add 0.05 BTC
});

let request = tonic::Request::new(EstimateDepositChannelFeeRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    channel_id: "ch_abc123".to_string(),
    asset_amounts,
    fee_rate: Some(FeeRate {
        relative: "5".to_string(),
    }),
});

let response = client.estimate_deposit_channel_fee(request).await?;
println!("Deposit fee: {}", response.into_inner().fee);

Deposit Channel

Deposit additional assets into an existing channel.

Method: DepositChannel

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
channel_idstringYESChannel to deposit to
asset_amountsmap<string, SendAmount>YESAssets to deposit
fee_rateFeeRateYESTransaction fee rate

Response:

FieldTypeDescription
txidstringDeposit transaction ID

Example Request:

TypeScript
const request = new DepositChannelRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setChannelId('ch_abc123')
request.setAssetAmountsMap({
  'BTC': { value: '5000000' }
})
request.setFeeRate({ relative: '5' })

const response = await client.depositChannel(request, {})
console.log('Deposit TX:', response.getTxid())
Go
req := &pb.DepositChannelRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    ChannelId: "ch_abc123",
    AssetAmounts: map[string]*pb.SendAmount{
        "BTC": {Value: "5000000"},
    },
    FeeRate: &pb.FeeRate{Relative: "5"},
}

resp, err := client.DepositChannel(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Deposit TX:", resp.Txid)
Rust
let mut asset_amounts = HashMap::new();
asset_amounts.insert("BTC".to_string(), SendAmount {
    value: "5000000".to_string(),
});

let request = tonic::Request::new(DepositChannelRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    channel_id: "ch_abc123".to_string(),
    asset_amounts,
    fee_rate: Some(FeeRate {
        relative: "5".to_string(),
    }),
});

let response = client.deposit_channel(request).await?;
println!("Deposit TX: {}", response.into_inner().txid);

Estimate Withdraw Channel Fee

Estimate fee for withdrawing assets from a channel.

Method: EstimateWithdrawChannelFee

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
channel_idstringYESChannel to withdraw from
asset_amountsmap<string, WithdrawAmount>YESAmounts to withdraw
fee_rateFeeRateYESTransaction fee rate

WithdrawAmount Object:

FieldTypeDescription
self_withdrawalSendAmountAmount you withdraw
counterparty_withdrawalSendAmountAmount counterparty withdraws

Response:

FieldTypeDescription
feeDecimalStringEstimated fee

Example Request:

TypeScript
const request = new EstimateWithdrawChannelFeeRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setChannelId('ch_abc123')
request.setAssetAmountsMap({
  'BTC': {
    selfWithdrawal: { value: '3000000' },
    counterpartyWithdrawal: { value: '2000000' }
  }
})
request.setFeeRate({ relative: '5' })

const response = await client.estimateWithdrawChannelFee(request, {})
console.log('Withdrawal fee:', response.getFee())
Go
req := &pb.EstimateWithdrawChannelFeeRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    ChannelId: "ch_abc123",
    AssetAmounts: map[string]*pb.WithdrawAmount{
        "BTC": {
            SelfWithdrawal:         &pb.SendAmount{Value: "3000000"},
            CounterpartyWithdrawal: &pb.SendAmount{Value: "2000000"},
        },
    },
    FeeRate: &pb.FeeRate{Relative: "5"},
}

resp, err := client.EstimateWithdrawChannelFee(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Withdrawal fee:", resp.Fee)
Rust
let mut asset_amounts = HashMap::new();
asset_amounts.insert("BTC".to_string(), WithdrawAmount {
    self_withdrawal: Some(SendAmount {
        value: "3000000".to_string(),
    }),
    counterparty_withdrawal: Some(SendAmount {
        value: "2000000".to_string(),
    }),
});

let request = tonic::Request::new(EstimateWithdrawChannelFeeRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    channel_id: "ch_abc123".to_string(),
    asset_amounts,
    fee_rate: Some(FeeRate {
        relative: "5".to_string(),
    }),
});

let response = client.estimate_withdraw_channel_fee(request).await?;
println!("Withdrawal fee: {}", response.into_inner().fee);

Withdraw Channel

Withdraw assets from an existing channel.

Method: WithdrawChannel

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
channel_idstringYESChannel to withdraw from
asset_amountsmap<string, WithdrawAmount>YESAmounts to withdraw
fee_rateFeeRateYESTransaction fee rate

Response:

FieldTypeDescription
txidstringWithdrawal transaction ID

Example Request:

TypeScript
const request = new WithdrawChannelRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setChannelId('ch_abc123')
request.setAssetAmountsMap({
  'BTC': {
    selfWithdrawal: { value: '3000000' },
    counterpartyWithdrawal: { value: '2000000' }
  }
})
request.setFeeRate({ relative: '5' })

const response = await client.withdrawChannel(request, {})
console.log('Withdrawal TX:', response.getTxid())
Go
req := &pb.WithdrawChannelRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    ChannelId: "ch_abc123",
    AssetAmounts: map[string]*pb.WithdrawAmount{
        "BTC": {
            SelfWithdrawal:         &pb.SendAmount{Value: "3000000"},
            CounterpartyWithdrawal: &pb.SendAmount{Value: "2000000"},
        },
    },
    FeeRate: &pb.FeeRate{Relative: "5"},
}

resp, err := client.WithdrawChannel(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Withdrawal TX:", resp.Txid)
Rust
let mut asset_amounts = HashMap::new();
asset_amounts.insert("BTC".to_string(), WithdrawAmount {
    self_withdrawal: Some(SendAmount {
        value: "3000000".to_string(),
    }),
    counterparty_withdrawal: Some(SendAmount {
        value: "2000000".to_string(),
    }),
});

let request = tonic::Request::new(WithdrawChannelRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    channel_id: "ch_abc123".to_string(),
    asset_amounts,
    fee_rate: Some(FeeRate {
        relative: "5".to_string(),
    }),
});

let response = client.withdraw_channel(request).await?;
println!("Withdrawal TX: {}", response.into_inner().txid);

Estimate Close Channel Fee

Estimate fee for cooperatively closing a channel.

Method: EstimateCloseChannelFee

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
channel_idstringYESChannel to close
asset_idsstring[]YESAssets to settle
fee_rateFeeRateYESTransaction fee rate

Response:

FieldTypeDescription
feeDecimalStringEstimated fee

Example Request:

TypeScript
const request = new EstimateCloseChannelFeeRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setChannelId('ch_abc123')
request.setAssetIdsList(['BTC'])
request.setFeeRate({ relative: '5' })

const response = await client.estimateCloseChannelFee(request, {})
console.log('Close fee:', response.getFee())
Go
req := &pb.EstimateCloseChannelFeeRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    ChannelId: "ch_abc123",
    AssetIds:  []string{"BTC"},
    FeeRate:   &pb.FeeRate{Relative: "5"},
}

resp, err := client.EstimateCloseChannelFee(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Close fee:", resp.Fee)
Rust
let request = tonic::Request::new(EstimateCloseChannelFeeRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    channel_id: "ch_abc123".to_string(),
    asset_ids: vec!["BTC".to_string()],
    fee_rate: Some(FeeRate {
        relative: "5".to_string(),
    }),
});

let response = client.estimate_close_channel_fee(request).await?;
println!("Close fee: {}", response.into_inner().fee);

Close Channel

Cooperatively close a channel with your peer.

Method: CloseChannel

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
channel_idstringYESChannel to close
asset_idsstring[]YESAssets to settle
fee_rateFeeRateYESTransaction fee rate

Response:

FieldTypeDescription
txidstringClosing transaction ID

Example Request:

TypeScript
const request = new CloseChannelRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setChannelId('ch_abc123')
request.setAssetIdsList(['BTC'])
request.setFeeRate({ relative: '5' })

const response = await client.closeChannel(request, {})
console.log('Channel closed, TX:', response.getTxid())
Go
req := &pb.CloseChannelRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    ChannelId: "ch_abc123",
    AssetIds:  []string{"BTC"},
    FeeRate:   &pb.FeeRate{Relative: "5"},
}

resp, err := client.CloseChannel(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Channel closed, TX:", resp.Txid)
Rust
let request = tonic::Request::new(CloseChannelRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    channel_id: "ch_abc123".to_string(),
    asset_ids: vec!["BTC".to_string()],
    fee_rate: Some(FeeRate {
        relative: "5".to_string(),
    }),
});

let response = client.close_channel(request).await?;
println!("Channel closed, TX: {}", response.into_inner().txid);

Estimate Force Close Channel Fee

Estimate fee for force-closing an unresponsive channel.

Method: EstimateForceCloseChannelFee

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
channel_idstringYESChannel to force close
asset_idsstring[]YESAssets to settle
fee_rateFeeRateYESTransaction fee rate

Response:

FieldTypeDescription
feeDecimalStringEstimated fee

Example Request:

TypeScript
const request = new EstimateForceCloseChannelFeeRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setChannelId('ch_abc123')
request.setAssetIdsList(['BTC'])
request.setFeeRate({ relative: '5' })

const response = await client.estimateForceCloseChannelFee(request, {})
console.log('Force close fee:', response.getFee())
Go
req := &pb.EstimateForceCloseChannelFeeRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    ChannelId: "ch_abc123",
    AssetIds:  []string{"BTC"},
    FeeRate:   &pb.FeeRate{Relative: "5"},
}

resp, err := client.EstimateForceCloseChannelFee(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Force close fee:", resp.Fee)
Rust
let request = tonic::Request::new(EstimateForceCloseChannelFeeRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    channel_id: "ch_abc123".to_string(),
    asset_ids: vec!["BTC".to_string()],
    fee_rate: Some(FeeRate {
        relative: "5".to_string(),
    }),
});

let response = client.estimate_force_close_channel_fee(request).await?;
println!("Force close fee: {}", response.into_inner().fee);

Force Close Channel

Force close a channel without peer cooperation.

Method: ForceCloseChannel

Important Notes:

  • Use only when peer is unresponsive
  • Assets locked until dispute period expires
  • May require additional RedeemClosedChannel transaction
  • Higher fees than cooperative close

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
channel_idstringYESChannel to force close
asset_idsstring[]YESAssets to settle
fee_rateFeeRateYESTransaction fee rate

Response:

FieldTypeDescription
txidstringForce close transaction ID

Example Request:

TypeScript
const request = new ForceCloseChannelRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setChannelId('ch_abc123')
request.setAssetIdsList(['BTC'])
request.setFeeRate({ relative: '10' }) // Higher fee recommended

const response = await client.forceCloseChannel(request, {})
console.log('Force close initiated, TX:', response.getTxid())
console.log('Assets will be spendable after dispute period')
Go
req := &pb.ForceCloseChannelRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    ChannelId: "ch_abc123",
    AssetIds:  []string{"BTC"},
    FeeRate:   &pb.FeeRate{Relative: "10"}, // Higher fee recommended
}

resp, err := client.ForceCloseChannel(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Force close initiated, TX:", resp.Txid)
log.Println("Assets will be spendable after dispute period")
Rust
let request = tonic::Request::new(ForceCloseChannelRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    channel_id: "ch_abc123".to_string(),
    asset_ids: vec!["BTC".to_string()],
    fee_rate: Some(FeeRate {
        relative: "10".to_string(), // Higher fee recommended
    }),
});

let response = client.force_close_channel(request).await?;
println!("Force close initiated, TX: {}", response.into_inner().txid);
println!("Assets will be spendable after dispute period");

Estimate Redeem Closed Channel Fee

Estimate fee for redeeming a force-closed channel.

Method: EstimateRedeemClosedChannelFee

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
channel_idstringYESClosed channel
asset_idsstring[]YESAssets to redeem
fee_rateFeeRateYESTransaction fee rate

Response:

FieldTypeDescription
feeDecimalStringEstimated fee

Example Request:

TypeScript
const request = new EstimateRedeemClosedChannelFeeRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setChannelId('ch_abc123')
request.setAssetIdsList(['BTC'])
request.setFeeRate({ relative: '5' })

const response = await client.estimateRedeemClosedChannelFee(request, {})
console.log('Redemption fee:', response.getFee())
Go
req := &pb.EstimateRedeemClosedChannelFeeRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    ChannelId: "ch_abc123",
    AssetIds:  []string{"BTC"},
    FeeRate:   &pb.FeeRate{Relative: "5"},
}

resp, err := client.EstimateRedeemClosedChannelFee(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Redemption fee:", resp.Fee)
Rust
let request = tonic::Request::new(EstimateRedeemClosedChannelFeeRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    channel_id: "ch_abc123".to_string(),
    asset_ids: vec!["BTC".to_string()],
    fee_rate: Some(FeeRate {
        relative: "5".to_string(),
    }),
});

let response = client.estimate_redeem_closed_channel_fee(request).await?;
println!("Redemption fee: {}", response.into_inner().fee);

Redeem Closed Channel

Redeem assets from a force-closed channel after dispute period.

Method: RedeemClosedChannel

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
channel_idstringYESClosed channel
asset_idsstring[]YESAssets to redeem
fee_rateFeeRateYESTransaction fee rate

Response:

FieldTypeDescription
txidstringRedemption transaction ID

Example Request:

TypeScript
const request = new RedeemClosedChannelRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setChannelId('ch_abc123')
request.setAssetIdsList(['BTC'])
request.setFeeRate({ relative: '5' })

const response = await client.redeemClosedChannel(request, {})
console.log('Assets redeemed, TX:', response.getTxid())
Go
req := &pb.RedeemClosedChannelRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    ChannelId: "ch_abc123",
    AssetIds:  []string{"BTC"},
    FeeRate:   &pb.FeeRate{Relative: "5"},
}

resp, err := client.RedeemClosedChannel(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Assets redeemed, TX:", resp.Txid)
Rust
let request = tonic::Request::new(RedeemClosedChannelRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    channel_id: "ch_abc123".to_string(),
    asset_ids: vec!["BTC".to_string()],
    fee_rate: Some(FeeRate {
        relative: "5".to_string(),
    }),
});

let response = client.redeem_closed_channel(request).await?;
println!("Assets redeemed, TX: {}", response.into_inner().txid);

Wait for Active Asset Channel

Wait for a channel's asset to become active for sending/receiving.

Method: WaitForActiveAssetChannel

Use Cases:

  • Wait after opening channel before sending payment
  • Ensure channel is ready after deposit
  • Verify channel state before operations

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
channel_idstringYESChannel to monitor
asset_idstringYESAsset to check
active_sendingboolYESWait for sending capability
active_receivingboolYESWait for receiving capability
updatableboolYESWait for updatable state

Response: Empty (resolves when conditions met)

Example Request:

TypeScript
const request = new WaitForActiveAssetChannelRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setChannelId('ch_abc123')
request.setAssetId('BTC')
request.setActiveSending(true)
request.setActiveReceiving(true)
request.setUpdatable(true)

console.log('Waiting for channel to be ready...')
await client.waitForActiveAssetChannel(request, {})
console.log('Channel is now active!')
Go
req := &pb.WaitForActiveAssetChannelRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    ChannelId:       "ch_abc123",
    AssetId:         "BTC",
    ActiveSending:   true,
    ActiveReceiving: true,
    Updatable:       true,
}

log.Println("Waiting for channel to be ready...")
_, err := client.WaitForActiveAssetChannel(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Channel is now active!")
Rust
let request = tonic::Request::new(WaitForActiveAssetChannelRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    channel_id: "ch_abc123".to_string(),
    asset_id: "BTC".to_string(),
    active_sending: true,
    active_receiving: true,
    updatable: true,
});

println!("Waiting for channel to be ready...");
client.wait_for_active_asset_channel(request).await?;
println!("Channel is now active!");

Allow Dual Funded Channel

Whitelist asset amounts for dual-funded channel opening.

Method: AllowDualFundedChannel

Benefits:

  • Single transaction for both parties
  • Lower total fees
  • Faster channel setup

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
node_idstringYESPeer's node ID
asset_amountsmap<string, DualFundAmount>YESWhitelisted amounts

DualFundAmount Object:

FieldTypeDescription
self_amountSendAmountYour contribution
counterparty_amountSendAmountPeer's contribution

Response: Empty (success confirmation)

Example Request:

TypeScript
const request = new AllowDualFundedChannelRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setNodeId('02abc123...')
request.setAssetAmountsMap({
  'BTC': {
    selfAmount: { value: '5000000' },      // You: 0.05 BTC
    counterpartyAmount: { value: '10000000' } // Peer: 0.1 BTC
  }
})

await client.allowDualFundedChannel(request, {})
console.log('Dual-fund whitelist updated')
Go
req := &pb.AllowDualFundedChannelRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    NodeId: "02abc123...",
    AssetAmounts: map[string]*pb.DualFundAmount{
        "BTC": {
            SelfAmount:         &pb.SendAmount{Value: "5000000"},      // You: 0.05 BTC
            CounterpartyAmount: &pb.SendAmount{Value: "10000000"}, // Peer: 0.1 BTC
        },
    },
}

_, err := client.AllowDualFundedChannel(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Dual-fund whitelist updated")
Rust
let mut asset_amounts = HashMap::new();
asset_amounts.insert("BTC".to_string(), DualFundAmount {
    self_amount: Some(SendAmount {
        value: "5000000".to_string(),      // You: 0.05 BTC
    }),
    counterparty_amount: Some(SendAmount {
        value: "10000000".to_string(), // Peer: 0.1 BTC
    }),
});

let request = tonic::Request::new(AllowDualFundedChannelRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    node_id: "02abc123...".to_string(),
    asset_amounts,
});

client.allow_dual_funded_channel(request).await?;
println!("Dual-fund whitelist updated");

Send Channel Payment

Send a direct payment through a specific channel.

Method: SendChannelPayment

Use Cases:

  • Direct peer payments
  • Testing channel functionality
  • Hashlock payments (HTLC)

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
channel_idstringYESChannel to use
asset_amountsmap<string, SendAmount>YESAssets to send
hashlockHashlockNOOptional hashlock for HTLC
expiry_timeout_secsuint64NOPayment expiry timeout

Hashlock Object:

FieldTypeDescription
payment_hashstringPayment hash (32 bytes hex)

Response:

FieldTypeDescription
payment_idstringPayment identifier

Example Request:

TypeScript
const request = new SendChannelPaymentRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setChannelId('ch_abc123')
request.setAssetAmountsMap({
  'BTC': { value: '100000' } // 0.001 BTC
})
// Optional: set hashlock for HTLC
// request.setHashlock({ paymentHash: 'abc123...' })
request.setExpiryTimeoutSecs(3600) // 1 hour

const response = await client.sendChannelPayment(request, {})
console.log('Payment sent:', response.getPaymentId())
Go
req := &pb.SendChannelPaymentRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    ChannelId: "ch_abc123",
    AssetAmounts: map[string]*pb.SendAmount{
        "BTC": {Value: "100000"}, // 0.001 BTC
    },
    // Optional: set hashlock for HTLC
    // Hashlock: &pb.Hashlock{PaymentHash: "abc123..."},
    ExpiryTimeoutSecs: 3600, // 1 hour
}

resp, err := client.SendChannelPayment(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Payment sent:", resp.PaymentId)
Rust
let mut asset_amounts = HashMap::new();
asset_amounts.insert("BTC".to_string(), SendAmount {
    value: "100000".to_string(), // 0.001 BTC
});

let request = tonic::Request::new(SendChannelPaymentRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    channel_id: "ch_abc123".to_string(),
    asset_amounts,
    // Optional: set hashlock for HTLC
    // hashlock: Some(Hashlock {
    //     payment_hash: "abc123...".to_string(),
    // }),
    expiry_timeout_secs: 3600, // 1 hour
});

let response = client.send_channel_payment(request).await?;
println!("Payment sent: {}", response.into_inner().payment_id);

Estimate Send Payment Fee

Estimate fee for a routed Lightning payment.

Method: EstimateSendPaymentFee

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
recipient_node_idstringYESRecipient's node ID
asset_amountsmap<string, SendAmount>YESAssets to send
hashlockHashlockNOOptional hashlock
expiry_timeout_secsuint64NOPayment timeout

Response:

FieldTypeDescription
feesmap<string, DecimalString>Fees per asset

Example Request:

TypeScript
const request = new EstimateSendPaymentFeeRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setRecipientNodeId('03def456...')
request.setAssetAmountsMap({
  'BTC': { value: '500000' }
})

const response = await client.estimateSendPaymentFee(request, {})
const fees = response.getFeesMap()
console.log('Routing fee:', fees.get('BTC'), 'sats')
Go
req := &pb.EstimateSendPaymentFeeRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    RecipientNodeId: "03def456...",
    AssetAmounts: map[string]*pb.SendAmount{
        "BTC": {Value: "500000"},
    },
}

resp, err := client.EstimateSendPaymentFee(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

fees := resp.Fees
log.Println("Routing fee:", fees["BTC"], "sats")
Rust
let mut asset_amounts = HashMap::new();
asset_amounts.insert("BTC".to_string(), SendAmount {
    value: "500000".to_string(),
});

let request = tonic::Request::new(EstimateSendPaymentFeeRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    recipient_node_id: "03def456...".to_string(),
    asset_amounts,
    hashlock: None,
    expiry_timeout_secs: 0,
});

let response = client.estimate_send_payment_fee(request).await?;
let fees = response.into_inner().fees;
println!("Routing fee: {} sats", fees.get("BTC").unwrap_or(&"0".to_string()));

Send Payment

Send a routed Lightning payment through the network.

Method: SendPayment

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
recipient_node_idstringYESRecipient's node ID
asset_amountsmap<string, SendAmount>YESAssets to send
hashlockHashlockNOOptional hashlock
expiry_timeout_secsuint64NOPayment timeout

Response:

FieldTypeDescription
payment_idstringPayment identifier

Example Request:

TypeScript
const request = new SendPaymentRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setRecipientNodeId('03def456...')
request.setAssetAmountsMap({
  'BTC': { value: '500000' }
})
request.setExpiryTimeoutSecs(3600)

const response = await client.sendPayment(request, {})
console.log('Payment routed:', response.getPaymentId())
Go
req := &pb.SendPaymentRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    RecipientNodeId:   "03def456...",
    AssetAmounts: map[string]*pb.SendAmount{
        "BTC": {Value: "500000"},
    },
    ExpiryTimeoutSecs: 3600,
}

resp, err := client.SendPayment(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Payment routed:", resp.PaymentId)
Rust
let mut asset_amounts = HashMap::new();
asset_amounts.insert("BTC".to_string(), SendAmount {
    value: "500000".to_string(),
});

let request = tonic::Request::new(SendPaymentRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    recipient_node_id: "03def456...".to_string(),
    asset_amounts,
    hashlock: None,
    expiry_timeout_secs: 3600,
});

let response = client.send_payment(request).await?;
println!("Payment routed: {}", response.into_inner().payment_id);

Create Invoice

Create a Lightning invoice for receiving payment.

Method: CreateInvoice

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
asset_idstringYESAsset to receive
amountDecimalStringNOAmount (omit for zero-amount invoice)
hashlockHashlockNOOptional hashlock
expiry_timeout_secsuint64NOInvoice expiry

Response:

FieldTypeDescription
invoiceInvoiceInvoice details

Invoice Object:

FieldTypeDescription
payment_requeststringEncoded payment request string
payment_hashstringPayment hash
amountDecimalStringInvoice amount (optional)
asset_idstringAsset identifier
expiryTimestampExpiry time

Example Request:

TypeScript
const request = new CreateInvoiceRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setAssetId('BTC')
request.setAmount('1000000') // 0.01 BTC
request.setExpiryTimeoutSecs(3600)

const response = await client.createInvoice(request, {})
const invoice = response.getInvoice()
console.log('Payment request:', invoice.getPaymentRequest())
console.log('Share this with the payer')
Go
req := &pb.CreateInvoiceRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    AssetId:           "BTC",
    Amount:            "1000000", // 0.01 BTC
    ExpiryTimeoutSecs: 3600,
}

resp, err := client.CreateInvoice(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

invoice := resp.Invoice
log.Println("Payment request:", invoice.PaymentRequest)
log.Println("Share this with the payer")
Rust
let request = tonic::Request::new(CreateInvoiceRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    asset_id: "BTC".to_string(),
    amount: "1000000".to_string(), // 0.01 BTC
    hashlock: None,
    expiry_timeout_secs: 3600,
});

let response = client.create_invoice(request).await?;
let invoice = response.into_inner().invoice.unwrap();
println!("Payment request: {}", invoice.payment_request);
println!("Share this with the payer");

Example (Zero-amount invoice):

TypeScript
const request = new CreateInvoiceRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setAssetId('BTC')
// No amount set - payer decides

const response = await client.createInvoice(request, {})
const invoice = response.getInvoice()
console.log('Zero-amount invoice:', invoice.getPaymentRequest())
Go
req := &pb.CreateInvoiceRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    AssetId: "BTC",
    // No amount set - payer decides
}

resp, err := client.CreateInvoice(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

invoice := resp.Invoice
log.Println("Zero-amount invoice:", invoice.PaymentRequest)
Rust
let request = tonic::Request::new(CreateInvoiceRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    asset_id: "BTC".to_string(),
    amount: String::new(), // No amount set - payer decides
    hashlock: None,
    expiry_timeout_secs: 0,
});

let response = client.create_invoice(request).await?;
let invoice = response.into_inner().invoice.unwrap();
println!("Zero-amount invoice: {}", invoice.payment_request);

Decode Invoice

Decode a Lightning invoice to view its details.

Method: DecodeInvoice

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
payment_requeststringYESEncoded payment request

Response:

FieldTypeDescription
invoiceInvoiceDecoded invoice details

Example Request:

TypeScript
const request = new DecodeInvoiceRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setPaymentRequest('lnbc10m1...')

const response = await client.decodeInvoice(request, {})
const invoice = response.getInvoice()
console.log('Amount:', invoice.getAmount())
console.log('Asset:', invoice.getAssetId())
console.log('Expires:', invoice.getExpiry())
Go
req := &pb.DecodeInvoiceRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    PaymentRequest: "lnbc10m1...",
}

resp, err := client.DecodeInvoice(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

invoice := resp.Invoice
log.Println("Amount:", invoice.Amount)
log.Println("Asset:", invoice.AssetId)
log.Println("Expires:", invoice.Expiry)
Rust
let request = tonic::Request::new(DecodeInvoiceRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    payment_request: "lnbc10m1...".to_string(),
});

let response = client.decode_invoice(request).await?;
let invoice = response.into_inner().invoice.unwrap();
println!("Amount: {}", invoice.amount);
println!("Asset: {}", invoice.asset_id);
println!("Expires: {:?}", invoice.expiry);

Estimate Pay Invoice Fee

Estimate routing fee for paying an invoice.

Method: EstimatePayInvoiceFee

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
payment_requeststringYESInvoice to pay

Response:

FieldTypeDescription
feeDecimalStringEstimated routing fee

Example Request:

TypeScript
const request = new EstimatePayInvoiceFeeRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setPaymentRequest('lnbc10m1...')

const response = await client.estimatePayInvoiceFee(request, {})
console.log('Routing fee:', response.getFee(), 'sats')
Go
req := &pb.EstimatePayInvoiceFeeRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    PaymentRequest: "lnbc10m1...",
}

resp, err := client.EstimatePayInvoiceFee(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Routing fee:", resp.Fee, "sats")
Rust
let request = tonic::Request::new(EstimatePayInvoiceFeeRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    payment_request: "lnbc10m1...".to_string(),
});

let response = client.estimate_pay_invoice_fee(request).await?;
println!("Routing fee: {} sats", response.into_inner().fee);

Pay Invoice

Pay a Lightning invoice.

Method: PayInvoice

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
payment_requeststringYESInvoice to pay

Response:

FieldTypeDescription
payment_idstringPayment identifier

Example Request:

TypeScript
const request = new PayInvoiceRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setPaymentRequest('lnbc10m1...')

const response = await client.payInvoice(request, {})
console.log('Payment sent:', response.getPaymentId())
Go
req := &pb.PayInvoiceRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    PaymentRequest: "lnbc10m1...",
}

resp, err := client.PayInvoice(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Payment sent:", resp.PaymentId)
Rust
let request = tonic::Request::new(PayInvoiceRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    payment_request: "lnbc10m1...".to_string(),
});

let response = client.pay_invoice(request).await?;
println!("Payment sent: {}", response.into_inner().payment_id);

Estimate Pay Empty Invoice Fee

Estimate fee for paying a zero-amount invoice with a specific amount.

Method: EstimatePayEmptyInvoiceFee

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
payment_requeststringYESZero-amount invoice
amountSendAmountYESAmount to pay

Response:

FieldTypeDescription
feeDecimalStringEstimated routing fee

Example Request:

TypeScript
const request = new EstimatePayEmptyInvoiceFeeRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setPaymentRequest('lnbc1...')
request.setAmount({ value: '2000000' }) // 0.02 BTC

const response = await client.estimatePayEmptyInvoiceFee(request, {})
console.log('Fee:', response.getFee())
Go
req := &pb.EstimatePayEmptyInvoiceFeeRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    PaymentRequest: "lnbc1...",
    Amount:         &pb.SendAmount{Value: "2000000"}, // 0.02 BTC
}

resp, err := client.EstimatePayEmptyInvoiceFee(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Fee:", resp.Fee)
Rust
let request = tonic::Request::new(EstimatePayEmptyInvoiceFeeRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    payment_request: "lnbc1...".to_string(),
    amount: Some(SendAmount {
        value: "2000000".to_string(), // 0.02 BTC
    }),
});

let response = client.estimate_pay_empty_invoice_fee(request).await?;
println!("Fee: {}", response.into_inner().fee);

Pay Empty Invoice

Pay a zero-amount invoice with a specific amount.

Method: PayEmptyInvoice

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
payment_requeststringYESZero-amount invoice
amountSendAmountYESAmount to pay

Response:

FieldTypeDescription
payment_idstringPayment identifier

Example Request:

TypeScript
const request = new PayEmptyInvoiceRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setPaymentRequest('lnbc1...')
request.setAmount({ value: '2000000' })

const response = await client.payEmptyInvoice(request, {})
console.log('Payment sent:', response.getPaymentId())
Go
req := &pb.PayEmptyInvoiceRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    PaymentRequest: "lnbc1...",
    Amount:         &pb.SendAmount{Value: "2000000"},
}

resp, err := client.PayEmptyInvoice(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Payment sent:", resp.PaymentId)
Rust
let request = tonic::Request::new(PayEmptyInvoiceRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    payment_request: "lnbc1...".to_string(),
    amount: Some(SendAmount {
        value: "2000000".to_string(),
    }),
});

let response = client.pay_empty_invoice(request).await?;
println!("Payment sent: {}", response.into_inner().payment_id);

Resolve Hashlock Payment

Claim a hashlock payment by revealing the preimage.

Method: ResolveHashlockPayment

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
payment_idstringYESPayment to resolve
payment_preimagestringYESPreimage (32 bytes hex)

Response: Empty (success confirmation)

Example Request:

TypeScript
const request = new ResolveHashlockPaymentRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setPaymentId('payment_abc123')
request.setPaymentPreimage('0123456789abcdef...')

await client.resolveHashlockPayment(request, {})
console.log('Payment claimed')
Go
req := &pb.ResolveHashlockPaymentRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    PaymentId:       "payment_abc123",
    PaymentPreimage: "0123456789abcdef...",
}

_, err := client.ResolveHashlockPayment(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Payment claimed")
Rust
let request = tonic::Request::new(ResolveHashlockPaymentRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    payment_id: "payment_abc123".to_string(),
    payment_preimage: "0123456789abcdef...".to_string(),
});

client.resolve_hashlock_payment(request).await?;
println!("Payment claimed");

Reject Payment

Reject an incoming payment.

Method: RejectPayment

Parameters:

NameTypeRequiredDescription
networkNetworkYESNetwork
payment_idstringYESPayment to reject

Response: Empty (success confirmation)

Example Request:

TypeScript
const request = new RejectPaymentRequest()
request.setNetwork({
  protocol: 0,
  chainId: '0',
  name: 'Bitcoin'
})
request.setPaymentId('payment_abc123')

await client.rejectPayment(request, {})
console.log('Payment rejected')
Go
req := &pb.RejectPaymentRequest{
    Network: &pb.Network{
        Protocol: 0,
        ChainId:  "0",
        Name:     "Bitcoin",
    },
    PaymentId: "payment_abc123",
}

_, err := client.RejectPayment(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Println("Payment rejected")
Rust
let request = tonic::Request::new(RejectPaymentRequest {
    network: Some(Network {
        protocol: 0,
        chain_id: "0".to_string(),
        name: "Bitcoin".to_string(),
    }),
    payment_id: "payment_abc123".to_string(),
});

client.reject_payment(request).await?;
println!("Payment rejected");

Common Workflows

Open channel and wait for activation

TypeScript
async function openAndWaitForChannel(
  client: NodeServiceClient,
  network: Network,
  nodeId: string,
  assetId: string,
  amount: string
) {
  // 1. Open channel
  const openReq = new OpenChannelRequest()
  openReq.setNetwork(network)
  openReq.setNodeId(nodeId)
  openReq.setAssetAmountsMap({
    [assetId]: { value: amount }
  })
  openReq.setFeeRate({ relative: '5' })

  const openResp = await client.openChannel(openReq, {})
  const channelId = openResp.getChannelId()
  console.log('Channel opened:', channelId)

  // 2. Wait for activation
  const waitReq = new WaitForActiveAssetChannelRequest()
  waitReq.setNetwork(network)
  waitReq.setChannelId(channelId)
  waitReq.setAssetId(assetId)
  waitReq.setActiveSending(true)
  waitReq.setActiveReceiving(true)
  waitReq.setUpdatable(true)

  console.log('Waiting for channel to become active...')
  await client.waitForActiveAssetChannel(waitReq, {})
  console.log('Channel is ready for payments!')

  return channelId
}
Go
func openAndWaitForChannel(
    client pb.NodeServiceClient,
    network *pb.Network,
    nodeId string,
    assetId string,
    amount string,
) (string, error) {
    // 1. Open channel
    openReq := &pb.OpenChannelRequest{
        Network: network,
        NodeId:  nodeId,
        AssetAmounts: map[string]*pb.SendAmount{
            assetId: {Value: amount},
        },
        FeeRate: &pb.FeeRate{Relative: "5"},
    }

    openResp, err := client.OpenChannel(context.Background(), openReq)
    if err != nil {
        return "", err
    }

    channelId := openResp.ChannelId
    log.Println("Channel opened:", channelId)

    // 2. Wait for activation
    waitReq := &pb.WaitForActiveAssetChannelRequest{
        Network:         network,
        ChannelId:       channelId,
        AssetId:         assetId,
        ActiveSending:   true,
        ActiveReceiving: true,
        Updatable:       true,
    }

    log.Println("Waiting for channel to become active...")
    _, err = client.WaitForActiveAssetChannel(context.Background(), waitReq)
    if err != nil {
        return "", err
    }

    log.Println("Channel is ready for payments!")
    return channelId, nil
}
Rust
async fn open_and_wait_for_channel(
    client: &mut NodeServiceClient<Channel>,
    network: Network,
    node_id: String,
    asset_id: String,
    amount: String,
) -> Result<String, Box<dyn std::error::Error>> {
    // 1. Open channel
    let mut asset_amounts = HashMap::new();
    asset_amounts.insert(asset_id.clone(), SendAmount {
        value: amount,
    });

    let open_req = tonic::Request::new(OpenChannelRequest {
        network: Some(network.clone()),
        node_id,
        asset_amounts,
        fee_rate: Some(FeeRate {
            relative: "5".to_string(),
        }),
    });

    let open_resp = client.open_channel(open_req).await?;
    let channel_id = open_resp.into_inner().channel_id;
    println!("Channel opened: {}", channel_id);

    // 2. Wait for activation
    let wait_req = tonic::Request::new(WaitForActiveAssetChannelRequest {
        network: Some(network),
        channel_id: channel_id.clone(),
        asset_id,
        active_sending: true,
        active_receiving: true,
        updatable: true,
    });

    println!("Waiting for channel to become active...");
    client.wait_for_active_asset_channel(wait_req).await?;
    println!("Channel is ready for payments!");

    Ok(channel_id)
}

Create and pay invoice

TypeScript
async function createAndPayInvoice(
  client: NodeServiceClient,
  network: Network,
  assetId: string,
  amount: string
) {
  // Create invoice
  const createReq = new CreateInvoiceRequest()
  createReq.setNetwork(network)
  createReq.setAssetId(assetId)
  createReq.setAmount(amount)
  createReq.setExpiryTimeoutSecs(3600)

  const createResp = await client.createInvoice(createReq, {})
  const invoice = createResp.getInvoice()
  const paymentRequest = invoice.getPaymentRequest()

  console.log('Invoice created:', paymentRequest)

  // Decode to verify
  const decodeReq = new DecodeInvoiceRequest()
  decodeReq.setNetwork(network)
  decodeReq.setPaymentRequest(paymentRequest)

  const decoded = await client.decodeInvoice(decodeReq, {})
  console.log('Amount:', decoded.getInvoice()?.getAmount())

  // Pay invoice
  const payReq = new PayInvoiceRequest()
  payReq.setNetwork(network)
  payReq.setPaymentRequest(paymentRequest)

  const payResp = await client.payInvoice(payReq, {})
  console.log('Payment sent:', payResp.getPaymentId())
}
Go
func createAndPayInvoice(
    client pb.NodeServiceClient,
    network *pb.Network,
    assetId string,
    amount string,
) error {
    // Create invoice
    createReq := &pb.CreateInvoiceRequest{
        Network:           network,
        AssetId:           assetId,
        Amount:            amount,
        ExpiryTimeoutSecs: 3600,
    }

    createResp, err := client.CreateInvoice(context.Background(), createReq)
    if err != nil {
        return err
    }

    invoice := createResp.Invoice
    paymentRequest := invoice.PaymentRequest
    log.Println("Invoice created:", paymentRequest)

    // Decode to verify
    decodeReq := &pb.DecodeInvoiceRequest{
        Network:        network,
        PaymentRequest: paymentRequest,
    }

    decoded, err := client.DecodeInvoice(context.Background(), decodeReq)
    if err != nil {
        return err
    }

    log.Println("Amount:", decoded.Invoice.Amount)

    // Pay invoice
    payReq := &pb.PayInvoiceRequest{
        Network:        network,
        PaymentRequest: paymentRequest,
    }

    payResp, err := client.PayInvoice(context.Background(), payReq)
    if err != nil {
        return err
    }

    log.Println("Payment sent:", payResp.PaymentId)
    return nil
}
Rust
async fn create_and_pay_invoice(
    client: &mut NodeServiceClient<Channel>,
    network: Network,
    asset_id: String,
    amount: String,
) -> Result<(), Box<dyn std::error::Error>> {
    // Create invoice
    let create_req = tonic::Request::new(CreateInvoiceRequest {
        network: Some(network.clone()),
        asset_id,
        amount,
        hashlock: None,
        expiry_timeout_secs: 3600,
    });

    let create_resp = client.create_invoice(create_req).await?;
    let invoice = create_resp.into_inner().invoice.unwrap();
    let payment_request = invoice.payment_request.clone();

    println!("Invoice created: {}", payment_request);

    // Decode to verify
    let decode_req = tonic::Request::new(DecodeInvoiceRequest {
        network: Some(network.clone()),
        payment_request: payment_request.clone(),
    });

    let decoded = client.decode_invoice(decode_req).await?;
    println!("Amount: {}", decoded.into_inner().invoice.unwrap().amount);

    // Pay invoice
    let pay_req = tonic::Request::new(PayInvoiceRequest {
        network: Some(network),
        payment_request,
    });

    let pay_resp = client.pay_invoice(pay_req).await?;
    println!("Payment sent: {}", pay_resp.into_inner().payment_id);

    Ok(())
}

Safe channel closure

TypeScript
async function safeCloseChannel(
  client: NodeServiceClient,
  network: Network,
  channelId: string,
  assetIds: string[]
) {
  // Try cooperative close first
  try {
    const closeReq = new CloseChannelRequest()
    closeReq.setNetwork(network)
    closeReq.setChannelId(channelId)
    closeReq.setAssetIdsList(assetIds)
    closeReq.setFeeRate({ relative: '5' })

    const response = await client.closeChannel(closeReq, {})
    console.log('Channel closed cooperatively:', response.getTxid())
    return
  } catch (error) {
    console.log('Cooperative close failed, trying force close...')
  }

  // Force close if cooperative fails
  const forceReq = new ForceCloseChannelRequest()
  forceReq.setNetwork(network)
  forceReq.setChannelId(channelId)
  forceReq.setAssetIdsList(assetIds)
  forceReq.setFeeRate({ relative: '10' })

  const forceResp = await client.forceCloseChannel(forceReq, {})
  console.log('Force close initiated:', forceResp.getTxid())
  console.log('Wait for dispute period before redeeming')
}
Go
func safeCloseChannel(
    client pb.NodeServiceClient,
    network *pb.Network,
    channelId string,
    assetIds []string,
) error {
    // Try cooperative close first
    closeReq := &pb.CloseChannelRequest{
        Network:   network,
        ChannelId: channelId,
        AssetIds:  assetIds,
        FeeRate:   &pb.FeeRate{Relative: "5"},
    }

    closeResp, err := client.CloseChannel(context.Background(), closeReq)
    if err == nil {
        log.Println("Channel closed cooperatively:", closeResp.Txid)
        return nil
    }

    log.Println("Cooperative close failed, trying force close...")

    // Force close if cooperative fails
    forceReq := &pb.ForceCloseChannelRequest{
        Network:   network,
        ChannelId: channelId,
        AssetIds:  assetIds,
        FeeRate:   &pb.FeeRate{Relative: "10"},
    }

    forceResp, err := client.ForceCloseChannel(context.Background(), forceReq)
    if err != nil {
        return err
    }

    log.Println("Force close initiated:", forceResp.Txid)
    log.Println("Wait for dispute period before redeeming")
    return nil
}
Rust
async fn safe_close_channel(
    client: &mut NodeServiceClient<Channel>,
    network: Network,
    channel_id: String,
    asset_ids: Vec<String>,
) -> Result<(), Box<dyn std::error::Error>> {
    // Try cooperative close first
    let close_req = tonic::Request::new(CloseChannelRequest {
        network: Some(network.clone()),
        channel_id: channel_id.clone(),
        asset_ids: asset_ids.clone(),
        fee_rate: Some(FeeRate {
            relative: "5".to_string(),
        }),
    });

    match client.close_channel(close_req).await {
        Ok(response) => {
            println!("Channel closed cooperatively: {}", response.into_inner().txid);
            return Ok(());
        }
        Err(_) => {
            println!("Cooperative close failed, trying force close...");
        }
    }

    // Force close if cooperative fails
    let force_req = tonic::Request::new(ForceCloseChannelRequest {
        network: Some(network),
        channel_id,
        asset_ids,
        fee_rate: Some(FeeRate {
            relative: "10".to_string(),
        }),
    });

    let force_resp = client.force_close_channel(force_req).await?;
    println!("Force close initiated: {}", force_resp.into_inner().txid);
    println!("Wait for dispute period before redeeming");

    Ok(())
}

Best Practices

  1. Always wait for channels to be active - Use WaitForActiveAssetChannel before sending
  2. Estimate fees first - Call estimate methods before operations
  3. Use cooperative close - Try CloseChannel before ForceCloseChannel
  4. Set reasonable timeouts - 1-24 hours for invoice expiry
  5. Monitor channel capacity - Ensure sufficient balance before sending
  6. Handle hashlock payments carefully - Keep preimage secret until ready
  7. Dual-fund when possible - More efficient than two separate channels

Channel Lifecycle

1. ConnectToPeer
   ↓
2. OpenChannel / AllowDualFundedChannel
   ↓
3. WaitForActiveAssetChannel
   ↓
4. SendPayment / CreateInvoice
   ↓
5. DepositChannel (if needed)
   ↓
6. WithdrawChannel (optional)
   ↓
7. CloseChannel / ForceCloseChannel
   ↓
8. RedeemClosedChannel (if force closed)

← Back to API Reference | Next: Rental API →


Copyright © 2025