NAV Navbar
shell python javascript

Lightning Loop REST API Reference

Welcome to the REST API reference documentation for Lightning Loop.

Lightning Loop is a non-custodial service offered by Lightning Labs to bridge on-chain and off-chain Bitcoin using submarine swaps. This repository is home to the Loop client and depends on the Lightning Network daemon lnd. All of lnd’s supported chain backends are fully supported when using the Loop client: Neutrino, Bitcoin Core, and btcd.

In the current iteration of the Loop software, only off-chain to on-chain exchanges are supported, where the Loop client sends funds off-chain in exchange for the funds back on-chain.

The service can be used in various situations:

Future iterations of the Loop software will also allow on-chain to off-chain swaps. These swaps can be useful for additional use-cases:

This site features the API documentation for shell script (CLI), Python and JavaScript clients in order to communicate with a local loopd instance through gRPC. Currently, this communication is unauthenticated, so exposing this service to the internet is not recommended.

The original client.proto file from which the gRPC documentation was generated can be found here.

NOTE: The byte field type must be set as the base64 encoded string representation of a raw byte array.

Alternatively, the gRPC documentation can be found here.

/v1/loop/out

$ curl -X POST http://localhost:8080/v1/loop/out -d '{ \
    "max_swap_routing_fee":<string>, \
    "sweep_conf_target":<int32>, \
    "max_miner_fee":<string>, \
    "max_prepay_routing_fee":<string>, \
    "max_swap_fee":<string>, \
    "max_prepay_amt":<string>, \
    "dest":<string>, \
    "amt":<string>, \
    "loop_out_channel":<string>, \
}'
{ 
    "id": <string>, 
    "htlc_address": <string>, 
}
>>> import base64, json, requests
>>> url = 'http://localhost:8080/v1/loop/out'
>>> data = { 
        'max_swap_routing_fee': <string>, 
        'sweep_conf_target': <int32>, 
        'max_miner_fee': <string>, 
        'max_prepay_routing_fee': <string>, 
        'max_swap_fee': <string>, 
        'max_prepay_amt': <string>, 
        'dest': <string>, 
        'amt': <string>, 
        'loop_out_channel': <string>, 
    }
>>> r = requests.post(url, verify=cert_path, data=json.dumps(data))
>>> print(r.json())
{ 
    "id": <string>, 
    "htlc_address": <string>, 
}
> var request = require('request');
> var requestBody = { 
    max_swap_routing_fee: <string>,
    sweep_conf_target: <int32>,
    max_miner_fee: <string>,
    max_prepay_routing_fee: <string>,
    max_swap_fee: <string>,
    max_prepay_amt: <string>,
    dest: <string>,
    amt: <string>,
    loop_out_channel: <string>,
  };
> var options = {
    url: 'http://localhost:8080/v1/loop/out',
    json: true,
    form: JSON.stringify(requestBody)
  };
> request.post(options, function(error, response, body) {
    console.log(body);
  });
{ 
    "id": <string>, 
    "htlc_address": <string>, 
}

POST /v1/loop/out

loop: out LoopOut initiates an loop out swap with the given parameters. The call returns after the swap has been set up with the swap server. From that point onwards, progress can be tracked via the SwapStatus stream that is returned from Monitor().

Field Type Placement Description
max_swap_routing_fee string body Maximum off-chain fee in msat that may be paid for payment to the server. This limit is applied during path finding. Typically this value is taken from the response of the GetQuote call.
sweep_conf_target int32 body The number of blocks from the on-chain HTLC's confirmation height that it should be swept within.
max_miner_fee string body Maximum in on-chain fees that we are willing to spent. If we want to sweep the on-chain htlc and the fee estimate turns out higher than this value, we cancel the swap. If the fee estimate is lower, we publish the sweep tx. If the sweep tx is not confirmed, we are forced to ratchet up fees until it is swept. Possibly even exceeding max_miner_fee if we get close to the htlc timeout. Because the initial publication revealed the preimage, we have no other choice. The server may already have pulled the off-chain htlc. Only when the fee becomes higher than the swap amount, we can only wait for fees to come down and hope - if we are past the timeout - that the server is not publishing the revocation. max_miner_fee is typically taken from the response of the GetQuote call.
max_prepay_routing_fee string body Maximum off-chain fee in msat that may be paid for payment to the server. This limit is applied during path finding. Typically this value is taken from the response of the GetQuote call.
max_swap_fee string body Maximum we are willing to pay the server for the swap. This value is not disclosed in the swap initiation call, but if the server asks for a higher fee, we abort the swap. Typically this value is taken from the response of the GetQuote call. It includes the prepay amount.
max_prepay_amt string body Maximum amount of the swap fee that may be charged as a prepayment.
dest string body Base58 encoded destination address for the swap.
amt string body Requested swap amount in sat. This does not include the swap and miner fee.
loop_out_channel string body The channel to loop out, the channel to loop out is selected based on the lowest routing fee for the swap payment to the server.

Response

Field Type Description
id string Swap identifier to track status in the update stream that is returned from the Start() call. Currently this is the hash that locks the htlcs.
htlc_address string The address of the on-chain htlc.

/v1/loop/in/quote

$ curl -X GET http://localhost:8080/v1/loop/in/quote/{amt}
{ 
    "swap_fee": <string>, 
    "miner_fee": <string>, 
    "prepay_amt": <string>, 
    "cltv_delta": <int32>, 
    "swap_payment_dest": <byte>, 
}
>>> import base64, json, requests
>>> url = 'http://localhost:8080/v1/loop/in/quote/{amt}'
>>> r = requests.get(url, verify=cert_path)
>>> print(r.json())
{ 
    "swap_fee": <string>, 
    "miner_fee": <string>, 
    "prepay_amt": <string>, 
    "cltv_delta": <int32>, 
    "swap_payment_dest": <byte>, 
}
> var request = require('request');
> var options = {
    url: 'http://localhost:8080/v1/loop/in/quote/{amt}',
    json: true
  };
> request.get(options, function(error, response, body) {
    console.log(body);
  });
{ 
    "swap_fee": <string>, 
    "miner_fee": <string>, 
    "prepay_amt": <string>, 
    "cltv_delta": <int32>, 
    "swap_payment_dest": <byte>, 
}

GET /v1/loop/in/quote/{amt}

GetQuote returns a quote for a swap with the provided parameters.

Field Type Placement Description
amt string path None
conf_target int32 query The confirmation target that should be used either for the sweep of the on-chain HTLC broadcast by the swap server in the case of a Loop Out, or for the confirmation of the on-chain HTLC broadcast by the swap client in the case of a Loop In.
external_htlc boolean query If external_htlc is true, we expect the htlc to be published by an external actor.

Response

Field Type Description
swap_fee string The fee that the swap server is charging for the swap.
miner_fee string An estimate of the on-chain fee that needs to be paid to sweep the HTLC.
prepay_amt string The part of the swap fee that is requested as a prepayment.
cltv_delta int32 On-chain cltv expiry delta
swap_payment_dest byte The node pubkey where the swap payment needs to be paid to. This can be used to test connectivity before initiating the swap.

/v1/loop/out/terms

$ curl -X GET http://localhost:8080/v1/loop/out/terms
{ 
    "min_swap_amount": <string>, 
    "max_swap_amount": <string>, 
}
>>> import base64, json, requests
>>> url = 'http://localhost:8080/v1/loop/out/terms'
>>> r = requests.get(url, verify=cert_path)
>>> print(r.json())
{ 
    "min_swap_amount": <string>, 
    "max_swap_amount": <string>, 
}
> var request = require('request');
> var options = {
    url: 'http://localhost:8080/v1/loop/out/terms',
    json: true
  };
> request.get(options, function(error, response, body) {
    console.log(body);
  });
{ 
    "min_swap_amount": <string>, 
    "max_swap_amount": <string>, 
}

GET /v1/loop/out/terms

loop: terms LoopOutTerms returns the terms that the server enforces for a loop out swap.

This request has no parameters.

Response

Field Type Description
min_swap_amount string Minimum swap amount (sat)
max_swap_amount string Maximum swap amount (sat)

/v1/loop/in

$ curl -X POST http://localhost:8080/v1/loop/in -d '{ \
    "max_swap_fee":<string>, \
    "loop_in_channel":<string>, \
    "max_miner_fee":<string>, \
    "amt":<string>, \
    "external_htlc":<boolean>, \
}'
{ 
    "id": <string>, 
    "htlc_address": <string>, 
}
>>> import base64, json, requests
>>> url = 'http://localhost:8080/v1/loop/in'
>>> data = { 
        'max_swap_fee': <string>, 
        'loop_in_channel': <string>, 
        'max_miner_fee': <string>, 
        'amt': <string>, 
        'external_htlc': <boolean>, 
    }
>>> r = requests.post(url, verify=cert_path, data=json.dumps(data))
>>> print(r.json())
{ 
    "id": <string>, 
    "htlc_address": <string>, 
}
> var request = require('request');
> var requestBody = { 
    max_swap_fee: <string>,
    loop_in_channel: <string>,
    max_miner_fee: <string>,
    amt: <string>,
    external_htlc: <boolean>,
  };
> var options = {
    url: 'http://localhost:8080/v1/loop/in',
    json: true,
    form: JSON.stringify(requestBody)
  };
> request.post(options, function(error, response, body) {
    console.log(body);
  });
{ 
    "id": <string>, 
    "htlc_address": <string>, 
}

POST /v1/loop/in

LoopIn initiates a loop in swap with the given parameters. The call returns after the swap has been set up with the swap server. From that point onwards, progress can be tracked via the SwapStatus stream that is returned from Monitor().

Field Type Placement Description
max_swap_fee string body Maximum we are willing to pay the server for the swap. This value is not disclosed in the swap initiation call, but if the server asks for a higher fee, we abort the swap. Typically this value is taken from the response of the GetQuote call.
loop_in_channel string body The channel to loop in. If zero, the channel to loop in is selected based on the lowest routing fee for the swap payment from the server. Note: NOT YET IMPLEMENTED
max_miner_fee string body Maximum in on-chain fees that we are willing to spent. If we want to publish the on-chain htlc and the fee estimate turns out higher than this value, we cancel the swap. max_miner_fee is typically taken from the response of the GetQuote call.
amt string body Requested swap amount in sat. This does not include the swap and miner fee.
external_htlc boolean body If external_htlc is true, we expect the htlc to be published by an external actor.

Response

Field Type Description
id string Swap identifier to track status in the update stream that is returned from the Start() call. Currently this is the hash that locks the htlcs.
htlc_address string The address of the on-chain htlc.

/v1/loop/out/quote

$ curl -X GET http://localhost:8080/v1/loop/out/quote/{amt}
{ 
    "swap_fee": <string>, 
    "miner_fee": <string>, 
    "prepay_amt": <string>, 
    "cltv_delta": <int32>, 
    "swap_payment_dest": <byte>, 
}
>>> import base64, json, requests
>>> url = 'http://localhost:8080/v1/loop/out/quote/{amt}'
>>> r = requests.get(url, verify=cert_path)
>>> print(r.json())
{ 
    "swap_fee": <string>, 
    "miner_fee": <string>, 
    "prepay_amt": <string>, 
    "cltv_delta": <int32>, 
    "swap_payment_dest": <byte>, 
}
> var request = require('request');
> var options = {
    url: 'http://localhost:8080/v1/loop/out/quote/{amt}',
    json: true
  };
> request.get(options, function(error, response, body) {
    console.log(body);
  });
{ 
    "swap_fee": <string>, 
    "miner_fee": <string>, 
    "prepay_amt": <string>, 
    "cltv_delta": <int32>, 
    "swap_payment_dest": <byte>, 
}

GET /v1/loop/out/quote/{amt}

loop: quote LoopOutQuote returns a quote for a loop out swap with the provided parameters.

Field Type Placement Description
amt string path None
conf_target int32 query The confirmation target that should be used either for the sweep of the on-chain HTLC broadcast by the swap server in the case of a Loop Out, or for the confirmation of the on-chain HTLC broadcast by the swap client in the case of a Loop In.
external_htlc boolean query If external_htlc is true, we expect the htlc to be published by an external actor.

Response

Field Type Description
swap_fee string The fee that the swap server is charging for the swap.
miner_fee string An estimate of the on-chain fee that needs to be paid to sweep the HTLC.
prepay_amt string The part of the swap fee that is requested as a prepayment.
cltv_delta int32 On-chain cltv expiry delta
swap_payment_dest byte The node pubkey where the swap payment needs to be paid to. This can be used to test connectivity before initiating the swap.

/v1/loop/in/terms

$ curl -X GET http://localhost:8080/v1/loop/in/terms
{ 
    "min_swap_amount": <string>, 
    "max_swap_amount": <string>, 
}
>>> import base64, json, requests
>>> url = 'http://localhost:8080/v1/loop/in/terms'
>>> r = requests.get(url, verify=cert_path)
>>> print(r.json())
{ 
    "min_swap_amount": <string>, 
    "max_swap_amount": <string>, 
}
> var request = require('request');
> var options = {
    url: 'http://localhost:8080/v1/loop/in/terms',
    json: true
  };
> request.get(options, function(error, response, body) {
    console.log(body);
  });
{ 
    "min_swap_amount": <string>, 
    "max_swap_amount": <string>, 
}

GET /v1/loop/in/terms

GetTerms returns the terms that the server enforces for swaps.

This request has no parameters.

Response

Field Type Description
min_swap_amount string Minimum swap amount (sat)
max_swap_amount string Maximum swap amount (sat)

Properties

SwapResponse

Field Type Description
id string Swap identifier to track status in the update stream that is returned from the Start() call. Currently this is the hash that locks the htlcs.
htlc_address string The address of the on-chain htlc.

SwapStatus

Field Type Description
cost_server string Swap server cost
htlc_address string Htlc address.
initiation_time string Initiation time of the swap.
last_update_time string Initiation time of the swap.
cost_offchain string Off-chain routing fees
type SwapType Swap type
id string Swap identifier to track status in the update stream that is returned from the Start() call. Currently this is the hash that locks the htlcs.
amt string Requested swap amount in sat. This does not include the swap and miner fee.
cost_onchain string On-chain transaction cost
state SwapState State the swap is currently in, see State enum.

LoopInRequest

Field Type Description
max_swap_fee string Maximum we are willing to pay the server for the swap. This value is not disclosed in the swap initiation call, but if the server asks for a higher fee, we abort the swap. Typically this value is taken from the response of the GetQuote call.
loop_in_channel string The channel to loop in. If zero, the channel to loop in is selected based on the lowest routing fee for the swap payment from the server. Note: NOT YET IMPLEMENTED
max_miner_fee string Maximum in on-chain fees that we are willing to spent. If we want to publish the on-chain htlc and the fee estimate turns out higher than this value, we cancel the swap. max_miner_fee is typically taken from the response of the GetQuote call.
amt string Requested swap amount in sat. This does not include the swap and miner fee.
external_htlc boolean If external_htlc is true, we expect the htlc to be published by an external actor.

LoopOutRequest

Field Type Description
max_swap_routing_fee string Maximum off-chain fee in msat that may be paid for payment to the server. This limit is applied during path finding. Typically this value is taken from the response of the GetQuote call.
sweep_conf_target int32 The number of blocks from the on-chain HTLC's confirmation height that it should be swept within.
max_miner_fee string Maximum in on-chain fees that we are willing to spent. If we want to sweep the on-chain htlc and the fee estimate turns out higher than this value, we cancel the swap. If the fee estimate is lower, we publish the sweep tx. If the sweep tx is not confirmed, we are forced to ratchet up fees until it is swept. Possibly even exceeding max_miner_fee if we get close to the htlc timeout. Because the initial publication revealed the preimage, we have no other choice. The server may already have pulled the off-chain htlc. Only when the fee becomes higher than the swap amount, we can only wait for fees to come down and hope - if we are past the timeout - that the server is not publishing the revocation. max_miner_fee is typically taken from the response of the GetQuote call.
max_prepay_routing_fee string Maximum off-chain fee in msat that may be paid for payment to the server. This limit is applied during path finding. Typically this value is taken from the response of the GetQuote call.
max_swap_fee string Maximum we are willing to pay the server for the swap. This value is not disclosed in the swap initiation call, but if the server asks for a higher fee, we abort the swap. Typically this value is taken from the response of the GetQuote call. It includes the prepay amount.
max_prepay_amt string Maximum amount of the swap fee that may be charged as a prepayment.
dest string Base58 encoded destination address for the swap.
amt string Requested swap amount in sat. This does not include the swap and miner fee.
loop_out_channel string The channel to loop out, the channel to loop out is selected based on the lowest routing fee for the swap payment to the server.

TermsResponse

Field Type Description
min_swap_amount string Minimum swap amount (sat)
max_swap_amount string Maximum swap amount (sat)

QuoteResponse

Field Type Description
swap_fee string The fee that the swap server is charging for the swap.
miner_fee string An estimate of the on-chain fee that needs to be paid to sweep the HTLC.
prepay_amt string The part of the swap fee that is requested as a prepayment.
cltv_delta int32 On-chain cltv expiry delta
swap_payment_dest byte The node pubkey where the swap payment needs to be paid to. This can be used to test connectivity before initiating the swap.

Enums

SwapState

Name Value
INITIATED 0
PREIMAGE_REVEALED 1
HTLC_PUBLISHED 2
SUCCESS 3
FAILED 4
INVOICE_SETTLED 5

SwapType

Name Value
LOOP_OUT 0
LOOP_IN 1