Watch-Only Node API
The Watch-Only Node API provides read-only access to Lightning Network node information without requiring node control.
Endpoints
- Get Node ID
- Get Channels
- Get Channels With Counterparty
- Get Channel
- Get Payments
- Get Pending Payments
- Get Payments By Hash
- Get Payment
Get Node ID
Get the node ID for a specific network.
Service: WatchOnlyNodeServiceMethod: GetNodeId
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Network to get node ID for |
Response:
| Field | Type | Description |
|---|---|---|
node_id | string | Lightning node public key |
Example Request:
TypeScript
const nodeId = await hydraGrpcClient.getNodeId({
protocol: Protocol.BITCOIN,
id: '0a03cf40' // Bitcoin Signet
})
Rust
let request = tonic::Request::new(GetNodeIdRequest {
network: Some(Network {
protocol: Protocol::Bitcoin as i32,
id: "0a03cf40".to_string(),
}),
});
let response = client.get_node_id(request).await?;
let node_id = response.into_inner().node_id;
Get Channels
Get all channels for a network.
Service: WatchOnlyNodeServiceMethod: GetChannels
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Network to query channels for |
Response:
| Field | Type | Description |
|---|---|---|
channels | Channel[] | Array of channel objects |
Channel Object:
| Field | Type | Description |
|---|---|---|
id | string | Channel identifier |
counterparty | string | Counterparty node ID |
status | ChannelStatus | Channel status |
asset_channels | map<string, AssetChannel> | Asset-specific channel data |
Example Request:
TypeScript
const channels = await hydraGrpcClient.getChannels({
protocol: Protocol.BITCOIN,
id: '0a03cf40'
})
channels.forEach(ch => {
console.log(`Channel ${ch.id} with ${ch.counterparty}`)
})
Rust
let request = tonic::Request::new(GetChannelsRequest {
network: Some(Network {
protocol: Protocol::Bitcoin as i32,
id: "0a03cf40".to_string(),
}),
});
let response = client.get_channels(request).await?;
let channels = response.into_inner().channels;
for channel in channels {
println!("Channel {} with {}", channel.id, channel.counterparty);
}
Get Channels With Counterparty
Get all channels with a specific counterparty node.
Service: WatchOnlyNodeServiceMethod: GetChannelsWithCounterparty
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Network to query |
counterparty_node_id | string | YES | Counterparty node public key |
Response:
| Field | Type | Description |
|---|---|---|
channels | Channel[] | Channels with this counterparty |
Example Request:
TypeScript
const channels = await hydraGrpcClient.getChannelsWithCounterparty({
network: {
protocol: Protocol.BITCOIN,
id: '0a03cf40'
},
counterpartyNodeId: '02abc123...'
})
Rust
let request = tonic::Request::new(GetChannelsWithCounterpartyRequest {
network: Some(Network {
protocol: Protocol::Bitcoin as i32,
id: "0a03cf40".to_string(),
}),
counterparty_node_id: "02abc123...".to_string(),
});
let response = client.get_channels_with_counterparty(request).await?;
let channels = response.into_inner().channels;
Get Channel
Get detailed information about a specific channel.
Service: WatchOnlyNodeServiceMethod: GetChannel
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Network the channel is on |
channel_id | string | YES | Channel identifier |
Response:
| Field | Type | Description |
|---|---|---|
channel | Channel | Detailed channel information |
Example Request:
TypeScript
const channel = await hydraGrpcClient.getChannel({
network: {
protocol: Protocol.BITCOIN,
id: '0a03cf40'
},
channelId: 'ch_abc123'
})
console.log(`Channel status: ${channel.status}`)
console.log(`Counterparty: ${channel.counterparty}`)
Rust
let request = tonic::Request::new(GetChannelRequest {
network: Some(Network {
protocol: Protocol::Bitcoin as i32,
id: "0a03cf40".to_string(),
}),
channel_id: "ch_abc123".to_string(),
});
let response = client.get_channel(request).await?;
let channel = response.into_inner().channel;
Get Payments
Get payment history for a network.
Service: WatchOnlyNodeServiceMethod: GetPayments
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Network to query payments for |
Response:
| Field | Type | Description |
|---|---|---|
payments | Payment[] | Array of payment objects |
Payment Object:
| Field | Type | Description |
|---|---|---|
id | string | Payment identifier |
direction | PaymentDirection | INCOMING or OUTGOING |
status | PaymentStatus | Payment status |
amount | map<string, DecimalString> | Asset amounts |
timestamp | Timestamp | Payment timestamp |
Example Request:
TypeScript
const payments = await hydraGrpcClient.getPayments({
protocol: Protocol.BITCOIN,
id: '0a03cf40'
})
payments.forEach(payment => {
console.log(`${payment.direction} payment: ${payment.id}`)
})
Rust
let request = tonic::Request::new(GetPaymentsRequest {
network: Some(Network {
protocol: Protocol::Bitcoin as i32,
id: "0a03cf40".to_string(),
}),
});
let response = client.get_payments(request).await?;
let payments = response.into_inner().payments;
for payment in payments {
println!("{:?} payment: {}", payment.direction, payment.id);
}
Get Pending Payments
Returns all pending (in-flight) off-chain payments on the specified network.
Method: GetPendingPayments
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Target network |
Response:
| Field | Type | Description |
|---|---|---|
payments | Payment[] | Currently in-flight payments |
Example Request:
import { GetPendingPaymentsRequest } from './proto/watch_only_node_pb'
const request = new GetPendingPaymentsRequest()
request.setNetwork({ protocol: 1, id: '0a03cf40' })
const response = await client.getPendingPayments(request, {})
const pending = response.getPaymentsList()
console.log(`${pending.length} pending payments`)
Get Payments By Hash
Returns all payments matching a specific payment hash. Useful for resolving the state of a known invoice or hashlock.
Method: GetPaymentsByHash
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Target network |
payment_hash | string | YES | Hex-encoded payment hash |
Response:
| Field | Type | Description |
|---|---|---|
payments | Payment[] | Payments matching the hash |
Example Request:
import { GetPaymentsByHashRequest } from './proto/watch_only_node_pb'
const request = new GetPaymentsByHashRequest()
request.setNetwork({ protocol: 1, id: '0a03cf40' })
request.setPaymentHash('a1b2c3d4...')
const response = await client.getPaymentsByHash(request, {})
console.log(`Found ${response.getPaymentsList().length} payments`)
Get Payment
Returns the details of a specific payment by its unique ID.
Method: GetPayment
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
network | Network | YES | Target network |
payment_id | string | YES | Unique payment identifier |
Response:
| Field | Type | Description |
|---|---|---|
payment | Payment | Requested payment |
Example Request:
import { GetPaymentRequest } from './proto/watch_only_node_pb'
const request = new GetPaymentRequest()
request.setNetwork({ protocol: 1, id: '0a03cf40' })
request.setPaymentId('pmt_abc123')
const response = await client.getPayment(request, {})
console.log('Payment:', response.getPayment()?.toObject())
Common Patterns
Monitor channel status
Rust
async fn get_active_channels(
client: &mut WatchOnlyNodeServiceClient<Channel>,
network: Network,
) -> Result<Vec<Channel>, Box<dyn std::error::Error>> {
let channels = client
.get_channels(GetChannelsRequest {
network: Some(network),
})
.await?
.into_inner()
.channels;
Ok(channels
.into_iter()
.filter(|ch| ch.status == ChannelStatus::Active as i32)
.collect())
}
Find channel with peer
Rust
async fn find_channel_with_peer(
client: &mut WatchOnlyNodeServiceClient<Channel>,
network: Network,
peer_id: String,
) -> Result<Option<Channel>, Box<dyn std::error::Error>> {
let channels = client
.get_channels_with_counterparty(GetChannelsWithCounterpartyRequest {
network: Some(network),
counterparty_node_id: peer_id,
})
.await?
.into_inner()
.channels;
Ok(channels.into_iter().next())
}
Error Handling
| Error Code | Description | Solution |
|---|---|---|
INVALID_ARGUMENT | Invalid network or channel ID | Verify parameters |
NOT_FOUND | Channel or payment not found | Check ID is correct |
UNAVAILABLE | Service temporarily unavailable | Retry with backoff |
Best Practices
- Read-only monitoring - Use this for monitoring without control
- Cache channel list - Channels don't change frequently
- Filter by status - Focus on active channels for routing
- Track payments - Monitor for payment reconciliation