diff --git a/track-payments.html b/track-payments.html
new file mode 100644
index 0000000..4f01802
--- /dev/null
+++ b/track-payments.html
@@ -0,0 +1,234 @@
+<!DOCTYPE html> 
+<html lang="en">
+<head>
+    <title>Bisq Repayment Report</title>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
+</head>
+<body>
+    <center>
+    <h2>Bisq Repayment Report</h2>
+    <small>Hard-coded input parameters, copied from: <a href="https://github.com/bisq-network/admin/issues/76">https://github.com/bisq-network/admin/issues/76</a>
+    <br>You can paste updated info into this box, or update this source file when new information is available.
+    <br>(NB: tabs are significant)</small>
+    <br>
+    <br>
+        <textarea rows=10 cols=72 id="phrase"></textarea>
+    <br>
+    <br>
+    <button id='START' onclick='startTest();'>run report</button>
+    <br>
+    <br>
+        <div id="results">results</div>
+    </center>
+</body>
+
+<script type="text/javascript">
+
+    window.readyHandlers = [];
+    window.ready = function ready(handler) {
+        window.readyHandlers.push(handler);
+        handleState();
+    };
+
+    window.handleState = function handleState () {
+        if (['interactive', 'complete'].indexOf(document.readyState) > -1) {
+            while(window.readyHandlers.length > 0) {
+                (window.readyHandlers.shift())();
+            }
+        }
+    };
+
+    document.onreadystatechange = window.handleState;
+
+    ready(function () {
+       // this hard-coded input data is cut-and-paste copied from
+       // https://github.com/bisq-network/admin/issues/76
+       // will need to update here whenever a change is made (i.e. a new address)
+        var str = 
+            "ID 	btc lost 	usd value 	repayment address\n"
+            +"1 	4.08 	$25,391.88 	1EU2aUQkHQukKbk3t5fmA4quHyMRBupM2U\n"
+            +"2 	4.60 	$31,126.54 	1ALWSRCKBuad6JUh7Y371uDPyH9nwbsvxZ\n"
+            +"3 	2.30 	$14,314.05 	1HpvvMHcoXQsX85CjTsco5ZAAMoGu2Mze9\n"
+            +"4 	8.80 	$54,766.80 	TBD\n"
+            +"5 	12.78 	$93,418.99 	19BNi5EpZhgBBWAt5ka7xWpJpX2ZWJEYyq\n"
+            +"6 	2.30 	$16,812.49 	TBD\n"
+            +"\n\n";
+        document.getElementById('phrase').value = str;
+    });
+
+
+    function startTest(){
+        jmcData.prepareData();
+        document.body.style.cursor = 'wait';
+        window.setTimeout(makeApiRequest,10);
+    }
+
+    function makeApiRequest() { // regular API request
+        // get a line from the input textarea
+        var addrs = jmcData.getAddressList();
+        if (listUnspentBlockchair(addrs) == true) {
+            document.getElementById('results').innerHTML = "calculating...\n";
+        }
+        else {
+            document.getElementById('results').innerHTML = "error\n";
+        }
+    }
+
+
+    // ===================================================================
+    // data manipulation functions
+
+    var jmcData = {
+
+        globalAccounts: null,
+
+        // grab the static source data required to make the report (repayment_address, etc)
+        prepareData: function() {
+            var strJson = '{"accounts": [ ';
+            var lines = document.getElementById('phrase').value.split('\n');
+            var count = 0;
+            while (lines.length > 0) {
+                var str = lines[0];
+                lines.splice(0,1);  // remove the first element
+                var fields = str.split('\t');
+                if (fields[0].trim() == "ID")
+                    continue;   // ignore header line
+                if (fields.length >= 4) {
+                    if (count > 0) { strJson += ','; }
+                    strJson += '{ "id":"'+ fields[0].trim() + '"';
+                    strJson += ', "btc_lost":"'+ fields[1].trim() + '"';
+                    strJson += ', "usd_value":"'+ fields[2].trim() + '"';
+                    strJson += ', "repayment_address":"'+ fields[3].trim() + '"';
+                    strJson += ', "btc_received":"0"';
+                    strJson += ', "usd_received":"0"';
+                    strJson += '}';
+                }
+                count = count + 1;
+            }
+            strJson += '], "transaction_count":"0"}';
+            globalAccounts = JSON.parse(strJson);
+
+        },
+
+        // get list of addresses for which we are reporting on
+        getAddressList: function() {
+            var retVal = "";
+            var all_info = globalAccounts.accounts;
+            for (const i of all_info) {
+                if (i.repayment_address != "TBD") {
+                    if (retVal.length > 0) { retVal += ","; }
+                    retVal += i.repayment_address;
+                }
+            }
+            return retVal;
+        },
+
+        // populate the report data with information retrieved from blockchair
+        setAmounts: function(repayment_address, btc_received, usd_received) {
+            var all_info = globalAccounts.accounts;
+            for (const i of all_info) {
+                if (i.repayment_address == repayment_address) {
+                    i.btc_received = btc_received;
+                    i.usd_received = usd_received;
+                }
+            }
+        },
+
+        // populate the report data with information retrieved from blockchair
+        setTransactionCount: function(transaction_count) {
+            globalAccounts.transaction_count = transaction_count;
+        },
+
+        // write the report into an HTML table
+        updateDocument: function() {
+            var results = "<table border=1 cellpadding=10>";
+                results += "<tr>"
+                    +"<td style='background-color:#a0a0a0'>ID</td>" 
+                    +"<td style='background-color:#a0a0a0'>Repayment address</td>" 
+                    +"<td style='background-color:#a0a0a0'>BTC lost</td>" 
+                    +"<td style='background-color:#a0a0a0'>BTC received</td>" 
+                    +"<td style='background-color:#a0a0a0'>USD value</td>" 
+                    +"<td style='background-color:#a0a0a0'>USD received</td></tr>\n";
+            var all_info = globalAccounts.accounts;
+            for (const i of all_info) {
+                results += "<tr>"
+                    +"<td>" + i.id.toString() + "</td>" 
+                    +"<td>" + i.repayment_address + "</td>" 
+                    +"<td>" + i.btc_lost.toString() + "</td>" 
+                    +"<td>" + i.btc_received.toString() + "</td>" 
+                    +"<td>" + i.usd_value.toString() + "</td>" 
+                    +"<td>$" + i.usd_received.toString() + "</td>" 
+                    +"</tr>\n";
+            }
+            results += "</table>\n";
+            results += "<br><br>Transaction count: " + globalAccounts.transaction_count.toString() + "<br>\n";
+            document.getElementById('results').innerHTML = results;
+            document.body.style.cursor = 'auto';
+        },
+
+
+    };  // end of jmcData
+
+
+
+    /* retrieve data from blockchair */
+    function listUnspentBlockchair(addr){
+        var options = {
+            url: "https://api.blockchair.com/bitcoin/dashboards/addresses/"+addr,
+            type: 'GET',
+            data: {},
+            dataType: 'json',
+            successCodes: [304, 401, 403, 404, 500],
+            statusCode: {},
+            success: [],
+            error: [],
+            complete: []
+        };
+
+        var req = new XMLHttpRequest();
+        var url = options.url;
+
+        req.addEventListener("progress", updateProgress);
+        req.addEventListener("load", transferComplete);
+        req.addEventListener("error", transferFailed);
+        req.addEventListener("abort", transferCanceled);
+
+        req.onreadystatechange = function () 
+        {
+            if (req.readyState > 3)
+            {
+                if (req.responseText.length > 0) {
+                    var data = JSON.parse(req.responseText);
+                    if ((data.context && data.data) && data.context.code =='200'){
+                        var all_info = data.data.addresses;
+                        for (var m in all_info){
+                            var o = all_info[m];
+                            var received = ((o.received.toString()*1)/100000000).toFixed(8);
+                            var received_usd = o.received_usd;
+                            jmcData.setAmounts(m, received, received_usd);
+                        }
+                        jmcData.setTransactionCount(data.data.set.transaction_count);
+                        jmcData.updateDocument();
+                    }
+                }
+            }
+        };
+
+        req.open(options.type, url, true);
+        req.send();
+
+        function updateProgress(evt) {
+        }
+        function transferComplete(evt) {
+        }
+        function transferFailed(evt) {
+        }
+        function transferCanceled(evt) {
+        }
+
+        return true;
+    }  // end of listUnspentBlockchair
+
+
+</script>
+</html>