AMP Guides and API

Blockstream AMP is an asset management platform that allows users to issue and manage Transfer Restricted assets or Issuer Tracked assets on the Liquid Network

Blockstream AMP is available as an API, allowing integration into existing systems and providing an easy way to expose end users to the asset capabilities of the Liquid Network.

The Blockstream AMP Case Studies help explain the different uses of Blockstream AMP.

Developer Quick Start

Details of how to obtain an API access token and then use it to authenticate against the AMP API can be found below.

More details of how to use the AMP API can be found in the Blockstream AMP API Tutorial. The API specification details all the AMP endpoints you can call to manage your AMP assets, registered users and access the reporting features.

import requests
import json

# TESTNET
API_URL='https://amp-test.blockstream.com/api'

username = 'your_amp_username'
password = 'your_amp_password'

url = f'{API_URL}/user/obtain_token'
headers = {'content-type': 'application/json'}
payload = {'username': username, 'password': password}
response = requests.post(url, data=json.dumps(payload), headers=headers)
assert response.status_code == 200
json_data = json.loads(response.text)
token = json_data['token']
print(token)
# You can now use the header for any AMP API calls:
header = {'content-type': 'application/json', 'Authorization': f'token {token}'}
print(header)

url = f'{API_URL}/assets/'
response = requests.get(url, headers=header)
assert response.status_code == 200
assets = json.loads(response.text)
print(assets)
const request = require('request');

let username = "your_amp_username";
let password = "your_amp_password";

data = { "username": username, "password": password }

options = {
    url: "https://amp-test.blockstream.com/api/user/obtain_token",
    method: "post",
    headers:
    {
     "Content-Type": "application/json"
    },
    body: JSON.stringify( data )
};

request(options, (error, response, body) => {
    if (error) {
        console.error('An error occurred: ', error);
    } else {
        json = JSON.parse(body);
        token = json.token;
        console.log(token);
        // Use the authorization token to call an endpoint requiring authorization
        let options = {
            url: `https://amp-test.blockstream.com/api/assets/`,
            method: "get",
            headers:
            {
             "Content-Type": "application/json",
             "Authorization": `token ${token}`
            }
        };
        request(options, (error, response, body) => {
            if (error) {
                console.error('An error occurred: ', error);
            } else {
                json = JSON.parse(body);
                console.log(json);
            }
        });
    };
});
require 'net/http'
require 'uri'
require 'json'

uri = URI('https://amp-test.blockstream.com/api/user/obtain_token')

username = 'your_amp_pGreen_QA_TEST_2'
password = 'R9nc63yTBv3y8msQ'

token = Net::HTTP.start(uri.host, uri.port, :use_ssl => uri.scheme == 'https') do |http|
  request = Net::HTTP::Post.new(uri, initheader = {'Content-Type' => 'application/json'})
  request.body = {username: "#{username}", password: "#{password}"}.to_json
  response = http.request request
  obj = JSON.parse(response.body)
  obj['token']
end
puts token

# Then use the token to call an endpoint requiring authorization here
# and using headers like this:
uri = URI('https://amp-test.blockstream.com/api/assets/')
req = Net::HTTP::Get.new(uri)
req['Content-Type'] = "application/json"
req['Authorization'] = "token #{token}"

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') { |http|
  http.request(req)
}
puts res.body
using System;
using System.Text;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace dotnetamp
{
    // Example classes to enable Serialization and Deserialization. This is
    // not required but may help you use the data returned in your code:
    public class ObtainTokenRequest
    {
        public string username { get; set; }
        public string password { get; set; }
    }

    public class ObtainTokenResponse
    {
        public string token { get; set; }
    }

    class Program
    {
        static async Task Main(string[] args)
        {
            // Initialize the HttpClient object with AMP test instance URL.
            HttpClient _client = new HttpClient { BaseAddress = new Uri("https://amp-test.blockstream.com/api/") };

            // An HTTP GET call that does not need Authorization:
            var result = await _client.GetAsync("info");

            // Get the result as a JSON string:
            var jsonResult = await result.Content.ReadAsStringAsync();
            Console.WriteLine(jsonResult);

            // To call endpoints that need authorization we first need to obtain an
            // Authorization token using the AMP account's username and password.
            // This also shows how to serialize and deserialize a request and
            // response, although that is not required, it can be useful.
            var tokenRequest = new ObtainTokenRequest()
            {
                username = "your_amp_username",
                password = "your_amp_password",
            };

            // Serialize the object and get the content as a JSON string to pass to the API:
            var jsonRequest = Newtonsoft.Json.JsonConvert.SerializeObject(tokenRequest);
            Console.WriteLine(jsonRequest);
            var content = new StringContent(jsonRequest, Encoding.Default, "application/json");

            // Make an HTTP POST call that to obtain the Authorization token:
            result = await _client.PostAsync("user/obtain_token", content);
            jsonResult = await result.Content.ReadAsStringAsync();

            string token = "";

            if (!result.IsSuccessStatusCode)
            {
                Console.WriteLine("FAILED TO GET TOKEN");
            }
            else
            {
                // Deserialize into an ObtainTokenResponse object:
                ObtainTokenResponse obtainTokenResponse = Newtonsoft.Json.JsonConvert.DeserializeObject<ObtainTokenResponse>(jsonResult);
                token = obtainTokenResponse.token;
                Console.WriteLine(token);
            }

            // Most calls require the use of the Authorization token obtained above
            // It must be set as the Authorization token header:
            _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("token", token);

            // NOTE: Using HttpClient, if you do not include the trailing / for calls to API that have
            // possilbe further paths e.g. /assets/{assetUuid} the call returns an authorization error.
            // Now we can make a call to an endpoint that requires authorization:
            result = await _client.GetAsync("assets/");

            // Here we will not Deserialize the result, just get it as a JSON string:
            jsonResult = await result.Content.ReadAsStringAsync();
            Console.WriteLine(jsonResult);

            // Once set, the Authorization header need not be set again within this conext and the
            // client will use it for subsequent calls:
            result = await _client.GetAsync("registered_users/");
            jsonResult = await result.Content.ReadAsStringAsync();
            Console.WriteLine(jsonResult);
        }
    }
}
package main

import (
    "log"
    "net/http"
    "encoding/json"
    "bytes"
    "io/ioutil"
    "fmt"
)

type Obtain_Token struct {
    Token string `json:"token"`
}

type Asset struct {
    Name string
}

func main() {
    log.SetFlags(0)
    
    url := "https://amp-test.blockstream.com/api/user/obtain_token"
    client := http.Client{}
    req , err := http.NewRequest("GET", url, nil)

    var jsonData = []byte(`{
        "username": "your_amp_username",
        "password": "your_amp_password"
    }`)

    req , err = http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
    req.Header = http.Header{
        "Content-Type": []string{"application/json"},
    }

    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }

    defer resp.Body.Close()
    
    ot := new(Obtain_Token)
    
    if err := json.NewDecoder(resp.Body).Decode(ot); err != nil {
        log.Fatalln(err)
    }

    log.Println(ot.Token)
    
    url = "https://amp-test.blockstream.com/api/assets/"

    req , err = http.NewRequest("GET", url, nil)

    if err != nil {
        log.Fatalln(err)
    }

    req.Header = http.Header{
        "Content-Type": []string{"application/json"},
        "Authorization": []string{"token " + ot.Token},
    }

    resp, err = client.Do(req)

    if err != nil {
        log.Fatalln(err)
    }

    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)

    if err != nil {
        log.Fatal(err)
    }

    var asset []Asset
    err = json.Unmarshal(body, &asset)
    fmt.Printf("%v\n", asset)
}

AMP Asset Types

Issuer Tracked

The Issuer Tracked asset type doesn’t place any restrictions on the recipient and there is no need for the issuer to register users in advance. Ownership can be taken by anyone who receives the asset to their Blockstream Green wallet’s ‘Managed Assets’ account. Transfers of the asset between Blockstream Green users is tracked by Blockstream AMP using the user’s Blockstream Green AMP ID. The AMP ID is also referred to as the Green Account ID (GAID) in some of the documentation here, along with the API spec. The two are the same. The issuer can view GAID balances for their asset, while the transactions remain private on the Liquid blockchain.

Transfer Restricted

The Transfer-Restricted asset type requires that the issuer registers the GAID of any potential owners within Blockstream AMP prior to the user being able to receive or transact it. This asset type can be used to issue and manage security tokens, amongst other things. This asset type also tracks and reports on ownership by GAID.

Holding AMP Assets in Blockstream Green

Users who receive AMP assets from the issuer need to hold them in a Blockstream Green compatible wallet.

Blockstream Green is available for both mobile and desktop. The Blockstream Green Development Kit (GDK) can be used if users want to programmatically manage a wallet’s Blockstream AMP assets. Examples are provided that show how to use GDK with Python and also how to use GDK from the command line.

A brief overview of Blockstream AMP can also be found on the Blockstream website.

Features and Benefits

  • Issue, assign, and distribute security tokens and other assets to registered users/investors using the transfer restricted asset type.
  • Apply category-based transfer restrictions to registered users.
  • Enable granular control of transaction restrictions by connecting Blockstream AMP to your own authorization API.
  • Report on user balances, ownership and asset activities.
  • Track reissuance and burn of an asset’s circulating supply.
  • Simplify the registration of an asset with the Liquid Asset Registry, for integration into Blockstream Green, Blockstream Explorer, and other supporting applications.
  • Issuers can delegate the management of their user and assignment data to third parties.
  • Blacklist asset UTXOs to cover user key-loss scenarios.
  • Manage the assignment and distribution of security tokens as vesting shares and record external dividend payments.
  • Calculate reward scheme payouts to asset owners.
  • Track Liquid’s Issued Assets with no need for registered user records with the issuer tracked asset type.

Before reading in more detail about Blockstream AMP and the asset types it allows you to manage, you might find it useful to refer to some commonly-used terminology and the features enabled by the Liquid Network. These can be found at the bottom of this page and in the Liquid section of this site.

Asset Tracking

Blockstream AMP relies on Blockstream Green to act as a co-signer to asset transfers, and so it stores the necessary information to record relevant transfer details, such as amounts and destinations. This enables issuers to produce reports, such as ‘proof of transfer’ and ‘proof of balance’, to auditors or regulators. Blockstream AMP also provides API endpoints that allow issuers to view transfer history, asset activities, and ownership, at any given Liquid block height.

All distributions, reissuances, burns, and transfers of Liquid Blockstream AMP managed assets that are held by the issuer’s Liquid node must be done using the Blockstream AMP API. This ensures that asset UTXOs remain tracked through Blockstream AMP.

Transfer Override Functions

For transfer restricted assets, Blockstream AMP implements transfer restrictions through the use of ‘whitelist’ categories. This process involves creating a category, assigning it to an asset, and then assigning users to the same category, which makes them eligible to receive the asset. Using a category, or set of categories, to manage user eligibility will handle most common use cases but occasionally issuers will have transfer restrictions that require additional logic or need certain transfers to be overruled. In these cases, Blockstream AMP lets an issuer define an API endpoint for each asset that will handle the authorization of transfer requests. When an ‘issuer authorization endpoint’ has been specified against an asset, Blockstream AMP passes Blockstream Green transfer requests to the issuer for approval. The result from that is then returned to the Blockstream Green server, bypassing any rules that might be defined within Blockstream AMP. This enables custom transfer rules to be developed by the issuer, allowing for the integration of business logic that does not fit the default category-based authorization model of Blockstream AMP. For more details, see the Issuer Authorization Override section.

Issuers are also able to manage a list of locked UTXOs, which prevents spends from a list of issuer-defined outputs. This is useful in scenarios such as when a user has lost access to their original wallet and requires a reissuance of the asset.

Blockstream AMP Asset Transfers

In order for users to receive and transact a Blockstream AMP managed asset, a user must have enabled a Managed Assets account in Blockstream Green. The Blockstream Green wallet ensures that the issuer can report on asset ownership and apply restrictions of transfer to the asset. The transfer rules for each type of asset are:

Transfer Restricted Assets

Transfers are restricted based upon Blockstream AMP registered user records created by the issuer. This can either be done through the use of categories or by an issuer providing their own validation logic, accessed via an ‘issuer authorization endpoint’. The receiving destination must be a valid Managed Assets account or one of the issuer’s own Liquid treasury addresses. Read more about Transfer Restricted Assets. This type of asset allows, amongst other things, security tokens to be managed through Blockstream AMP. In order to apply the rules needed to conform to regulatory requirements, Blockstream AMP controls the issuance and transfer of these assets.

Issuer Tracked Assets

Issuers may want to issue an asset that only uses Blockstream AMP to track ownership and transfers. In this set up, transfers are only restricted by requiring that the receiving destination must be a valid Managed Assets account or one of the issuer’s own Liquid treasury addresses. registered users and category records are not used. Although there may be external restrictions placed on the users through the use of an ‘issuer authorization endpoint’, the lack of a Blockstream AMP registered user record means we refer to them as ‘Issuer Tracked’ assets, as transfers are only tracked and not restricted in any other way by Blockstream AMP. Read more about Issuer Tracked Assets.

Useful Liquid Terms

As Blockstream AMP’s assets are issued and transacted on the Liquid Network it is useful to familiarize yourself with some of the features of Liquid. Some of the main features of Liquid used by Blockstream AMP are outlined below.

Liquid is a Bitcoin sidechain built using the open-source Elements software. Liquid uses a federated consensus model which allows for rapid settlement of both Bitcoin and Issued Assets. Liquid also utilizes Confidential Transactions, which allows the asset amount and type to be hidden from any actors not directly involved in the transaction. Liquid also provides the ability to reveal any of this information to select third parties, such as auditors and regulators.

When managed through Blockstream AMP, those assets can represent the tokenized, digital form of traditional securities, a digital stimulus payment, a claim on gold, or any number of other use cases.

Asset Issuance

The act of creating a new asset on the Liquid Network is referred to as Asset Issuance. All Liquid assets are transferable between Liquid users and make use of Liquid network features like Confidential Transactions.

Issuers can also add their asset to the Liquid Asset Registry, making it available to services that use the registry data, such as Blockstream Green. Issuers who want this option enabled must provide a ticker, the domain name used to validate ownership, and a public key during asset issuance.

Blockstream AMP uses Liquid to issue assets directly into an issuer’s wallet, it never holds any assets or reissuance tokens on the issuer’s behalf.

Asset Reissuance

An asset can be created as reissuable during asset issuance. Reissuance is the act of creating more of an existing asset. The reissuance of additional amounts of existing assets is controlled by a special kind of asset, the reissuance token, which is created alongside the actual asset during issuance. Reissuance tokens can only be created during initial asset issuance.

Reissuance tokens act as a verifiable right to increase the circulating amount of the associated asset. As with any other asset, they are exchangeable among participants on the network.

🚧

Care must be taken to ensure that only those authorized to reissue an asset are in possession of the reissuance token.

Blockstream AMP introduces some concepts that don’t apply to all assets on the Liquid network, only to those managed within Blockstream AMP. These are explained within this page.