Introduction

Find everything you might need to know about our product 21 Travel Rule here.

On-premises

21 Travel Rule is on-premises software that you can license from 21 Analytics. At no time is information exposed or sent to 21 Analytics!

Multiprotocol

It is a multiprotocol FATF Travel Rule solution. Currently, the supported protocols are:

  • TRP
  • VerifyVASP
  • Email

When other protocols gain traction in the market, we add them.

GUI

We offer a GUI for your compliance officer to handle incoming and outgoing Travel Rule messages. Managing VASP-to-VASP messages is done in the GUI. The same goes for address ownership proofs. The GUI is most suited for parties that have a limited number of transactions per day or parties who have integrated with our API and want a dashboard like functionality.

API

Additionally, a GraphQL API is available to integrate our software into your IT infrastructure. This requires more effort but can handle a large number of transactions. In addition, this allows for a high level of automation. For example, it is possible to automatically approve transactions that are below a certain threshold.

Deployment

We have documentation on the deployment of our software and subsequent upgrades. You are using Docker to deploy software in your infrastructure. Once a licence agreement is signed, we will give you access to our registry. We advise running at least two instances of our software - one for production and one for testing. Additionally, staging and failover instances should be run in an enterprise environment.

Mirror

We mirror some information you will find here. When we do, we will point to the authoritative source.

Questions

Please direct questions to info@21analytics.ch.

Deployment

Authoritative source

21 Travel Rule: Reference Deployment

21 Travel Rule is a software solution for FATF’s Travel Rule by 21 Analytics.

These instructions only work if you have a valid 21 Travel Rule license, which comes with a username and password to access our Docker Registry.

If you get stuck with the deployment instructions then please refer to our Troubleshooting section where common usage errors are clarified.

Hardware Requirements

We recommend the following minimum for operating the 21 Travel Rule software:

  • 2 CPU 2198 MHz
  • 2 GB RAM
  • 40 GB Disk

The hardware requirements depend the number of transactions you send & receive.

The above disk requirement includes 20 GB dedicated to the file storage service. You may in- or decrease this quota by modifying the arguments to the seaweedfs service.

Software Requirements

To run 21 Travel Rule the following software needs to be installed:

  • Docker
  • Docker-Compose (version 2.x or higher)
  • git

Any operating system which can run Linux Docker containers will work.

Deployment with Docker-Compose

First, pull this git repository with

git clone https://gitlab.com/21analytics/21-travel-deployment.git

Second, login to our Docker Registry using the username / password that you have obtained from us by executing the following command:

docker login registry.21analytics.ch -u 'YourUsername' # single-quotes are important!

Then adjust the domain names in the Caddyfile to enable HTTPS. You probably want to commit your changes to the Caddyfile to simplify upgrades later:

git add Caddyfile
git commit -m "Caddyfile: Set domain"

After that, you can spin up your instance with the docker-compose file as shown below. The first time you run those commands the database access passwords are initialized. Therefore, you are free to choose those passwords. We recommend generating cryptographically secure passwords with your chosen key management solution.

After the first initialization, the environment variables still need to be set to successfully start the platform. The POSTGRES_PASSWORD can be omitted after the first initialization. Instead of exporting the environment variables you can use a .env file, see here. Also, the pg_data folder needs to be created where the application data is persisted.

export POSTGRES_PASSWORD=secret_password_1 # only required for init
export AUTOD_DB_PW=secret_password_2
export AOPD_DB_PW=secret_password_3
export TRPD_DB_PW=secret_password_4
export TRAVEL_LOG=info
mkdir pg_data
docker-compose up -d

You probably don't want to use the master branch. It contains the latest versions from our development branch which contain work-in-progress features.

Once the services are up and running, a user can be created by accessing the graphical user interface. After the first login, the user is redirected to the settings page where further details should be configured.

Logging

All our services emit log messages. The log level can be adjusted by setting the TRAVEL_LOG environment variable. Starting with the least verbose level the available log levels are:

  1. error
  2. warn
  3. info
  4. debug
  5. trace

where info is the default log level. TRAVEL_LOG=debug increases the logging level to debug.

Further, it is possible to selectively adjust the logging level for certain modules only (the module names can be obtained from existing logging output), e.g. to increase the logging level for HTTP traffic TRAVEL_LOG=tower_http::trace=debug should be set.

Putting all together the services can be run with a adjusted logging level for HTTP traffic like demonstrated in the following command.

TRAVEL_LOG=tower_http::trace=debug docker-compose up -d

Graphical User Interface

The graphical user interface can be accessed at port 3000 by default. It needs to be served from the root path /. If you decide to serve the graphical user interface on the public internet (not recommended) then a custom subdomain can be used to avoid collisions with other applications that also make use of the root path.

Upgrade

Move into the directory where you previously executed the install commands. Then do:

git pull --rebase
docker-compose pull
docker-compose up -d

Please note that you likely need to point to a new Docker Compose file.

APIs exposed by Caddy reverse proxy

Here, we document the API endpoints that require to be publicly accessible. Our reference Caddy configuration in the Caddyfile already sets up everything accordingly. This is meant as a reference for firewall and WAF (web application firewall) configuration.

Travel Rule Protocol (TRP):
443 (HTTPS) at /transfers and /transfers/
TCP Incoming and Outgoing. This has to be accessible for your counterparty VASPs.

AOPP 443 (HTTPS) at /proofs/
TCP, incoming only. This has to be accessible for your customers.

Webinterface:
3000 (default setting)
TCP used internally only (by the VASP).
Make sure nobody can access this from outside of your organization.

Working with OpenShift/Kubernetes

Disclaimer: we don't offer support for deployments on OpenShift or Kubernetes due to the large diversity of possible architectures. However, we have found that our reference deployment offers a helpful guideline for deployments on OpenShift/Kubernetes. Therefore, we provide some hints below for how the reference deployment can be efficiently transformed for use on OpenShift or Kubernetes.

Converting the docker-compose.yml

You can use the kompose tool to convert the docker-compose.yml. Often, the generated files need some manual adjustments, e.g. you might want to remove the caddy reverse proxy service because you are already running a different solution.

Configuring your reverse proxy

You very likely have a reverse proxy running in your cluster already. The Caddyfile can be inspected to extract configuration details you need to apply for your reverse proxy.

Using an existing Postgres database

The init.db script can be inspected to extract the required configuration for Postgres (users, passwords, schemas, permissions). As a consequence the database connection URLs needs to be changed that are passed to the 21 Travel Rule services.

Troubleshooting

I'm seeing a Python traceback when running docker-compose

The output you see looks similar to

Traceback (most recent call last):
File "urllib3/connectionpool.py", line 670, in urlopen
File "urllib3/connectionpool.py", line 392, in _make_request
File "http/client.py", line 1255, in request
File "http/client.py", line 1301, in _send_request
File "http/client.py", line 1250, in endheaders
File "http/client.py", line 1010, in _send_output
File "http/client.py", line 950, in send
File "docker/transport/unixconn.py", line 43, in connect
FileNotFoundError: [Errno 2] No such file or directory

Those errors are usually encountered when the docker service is not running on your machine.

I'm using docker-compose with sudo and the environment variables are not set

sudo runs commands as a different user and doesn't preserve the original user's environment unless run using the --preserve-env flag. With that said, nowadays docker and docker-compose is commonly packaged such that it doesn't require sudo for execution. That's why our examples don't display the usage of sudo.

I'm using an .env file and the variables are not properly set

You've likely pasted the environment variables with a leading export command from our shell example snippet. Shell commands don't work in .env files and need to be omitted.

I'm getting the unhelpful 'Killed' error message

Your machine runs out of memory while starting the containers. Consider using a more powerful instance. 1GB is a minimum that is known to work.

I'm seeing error messages in the SeaweedFS Container

At the start, the SeaweedFS container repeatedly shows two error messages in the log (rpc error and missing pemfile). Both can safely be ignored when using the reference deployment as they are triggered by its internal service being started while polling on each other.

I'm unable to log in to the registry: 'Cannot autolaunch D-Bus'

Docker needs something to store your credentials in. On Linux this is pass. If this is not installed you might see this error:

Error saving credentials: error storing credentials - err: exit status 1, out: `Cannot autolaunch D-Bus without X11 $DISPLAY`

Simply install pass to resolve this issue.

Security

In this section we give an overview of our security processes, designs and mitigations.

Security-first development approach

Rust

We use Rust as our backend development language. Rust is a memory-safe language that compiles to machine code and thus doesn't compromise on performance while eliminating the primary source for security-related bugs [1][2][3].

Rust has seen growing adoption in both critical open source software [4] [5] [6] [7] and at Fortune 500 companies [8] [9] [10] [11].

Code Review

We have strict code review policies in place that demand that every change to the source code needs to be reviewed and approved by another developer. Only then it can be merged into master. Among other aspects the reviewer is asked to focus on security. As a side-effect of our code review practices, know-how is spread across the team that shares the responsibility for the source code. This eliminates malware injection or sabotage from malicious insiders.

Continuous Integration

Our continuous integration pipeline runs tests, linter checks and builds. Only if all tasks run successfully a change can be merged into master.

Employing a security-in-depth approach, we use the cargo-audit [12] vulnerability scanner to automatically scan our entire dependency tree for versions with reported security vulnerabilities. This is also run by the continuous integration system and hence, will block offending changes from being merged into master. If issues arise on the master branch then all merge requests will we blocked until the issue has been fixed on master.

Dependency Management

We use tools that monitor our dependencies (libraries, docker images, etc.) and open merge requests once newer versions of dependencies become available. After they pass our continuous integration they are reviewed and can be confidently merged into master without introducing regressions or breaking the build. Updates that fail continuous integration are handled manually to ensure a quick update.

Having an efficient and effortless dependency management process enables us to constantly ship up-to-date software to our customers.

Security by Design

Secure Architecture

Separation of Concerns

We have designed a micro service architecture where we apply the separation of concerns [13] principle. APIs accessible from the public internet are exposed in different micro services than APIs that interface with the custodial system.

In our reference deployment [14] those logical separations are enforced with a configuration that also splits them on a network level.

Principle of least privilege

Our services are stateless and use a database for persistence. We use database roles and permissions in accordance to the principle of least privilege [15] to refuse one microservice from accessing another's data.

Mitigation

We are aware that even when a memory-safe language like Rust is used disaster can strike nonetheless. Our services are containerized to isolate them from the host system. Furthermore, we statically compile our binaries which allows us to run them in empty containers. This significantly limits the attack surface in case of a successful memory-corruption exploit. For example, an attacker could not even gain access to libc or any binary useful for escalating the exploit chain like e.g. a shell.

Resources

[1] https://www.chromium.org/Home/chromium-security/memory-safety/
[2] https://www.zdnet.com/article/microsoft-70-percent-of-all-security-bugs-are-memory-safety-issues/
[3] https://security.googleblog.com/2021/02/mitigating-memory-safety-issues-in-open.html
[4] https://daniel.haxx.se/blog/2021/02/09/curl-supports-rustls/
[5] https://security.googleblog.com/2021/04/rust-in-linux-kernel.html
[6] https://security.googleblog.com/2021/04/rust-in-android-platform.html
[7] https://www.abetterinternet.org/post/preparing-rustls-for-wider-adoption/
[8] https://learn.microsoft.com/en-us/windows/dev-environment/rust/rust-for-windows
[9] https://engineering.fb.com/2021/04/29/developer-tools/rust/
[10] https://aws.amazon.com/blogs/opensource/why-aws-loves-rust-and-how-wed-like-to-help/
[11] https://aws.amazon.com/blogs/opensource/sustainability-with-rust/
[12] https://rustsec.org/
[13] https://en.wikipedia.org/wiki/Separation_of_concerns
[14] https://docs.21analytics.ch/deployment.html
[15] https://en.wikipedia.org/wiki/Principle_of_least_privilege

Travel Rule

To understand the purpose of our software, it is fundamental to grasp what the Travel Rule is. The Travel Rule is a recommendation set up by the Financial Action Task Force (FATF) to address Money Laundering and Terrorist Financing. According to the Travel Rule, specific personal data must be exchanged with virtual asset transactions. As the Travel Rule is a recommendation countries are not obliged to implement it by a specific date or at all, resulting in global mismatched implementation statuses. Additionally, implementing regions can deviate from the recommendation in terms of information exchanged or when information should be exchanged. Once implemented by a region, the Travel Rule becomes law, and not exchanging this information will be considered illegal. In other words, virtual asset service providers (VASPs) like exchanges and banks will be obliged to collect and store the required customer information.

Gathering, tracking and storing this information can become tedious and unreliable if not done correctly. This is where software like 21 Travel Rule comes into play. Our software allows VASP compliance teams to easily manage and store all this information for each transaction processed.

Address Ownership

Address ownership tackles a specific problem within the Travel Rule: self-hosted wallet transactions. Unlike virtual asset service providers (VASPs), which hold virtual assets on behalf of the owner, self-hosted wallets allow the owner to hold virtual assets and control the private key used to transact themselves.

Self-hosted wallets are a greater challenge when it comes to requesting and storing personal data since there is no organisation behind it, and expecting every individual who owns a wallet to store the personal information of their counterparts and the transaction is unfeasible. Moreover, it would pose a big risk regarding data security.

The closest solution so far is requesting to prove ownership of a self-hosted wallet when depositing or withdrawing from a VASP. This means VASPs have the responsibility to validate that their customer owns a given self-hosted wallet and store this proof.

Travel Rule Protocol (TRP) Flow

To solve the Travel Rule requirements, numerous protocols have been created. The Travel Rule Protocol (TRP) is, in our opinion, the cleanest and all-around most streamlined solution. This is why the 21 Analytics team has decided to contribute to its development and evolution.

The 21 Product Family has been working on making the TRP protocol as straightforward as possible while focusing strongly on security with the product userflow. Security is the cornerstone of TRP since financial data associated with personal information can be highly compromising for individuals, so privacy is a must.

Let’s now break down how this userflow works to get a deeper understanding of how to use the 21 Product Family products:

  1. A beneficiary VASP creates a Travel Address for one of its users.

  2. An originator VASP sends a transaction request to a beneficiary VASP using this Travel Address.

  3. The beneficiary VASP runs chosen regulatory and security checks on the originator User. If it gets declined, this is then archived.

  4. If the beneficiary VASP approves, the originator VASP performs the on chain transaction and notifies the beneficiary of the transaction by clicking on Confirm. The originator can also opt to not perform the on chain transaction and notify the beneficiary by clicking on Cancel.

Bear in mind this is an explanation of the userflow in our product; for a more in-depth explanation, of the protocol check TRP.

Terminology

Travel Address

The beneficiary VASP generates a Travel Address. It is a string of alphanumeric digits that, when decoded, provide information on the linked account, such as:

  • To which VASP the coins are to be sent,
  • Where the FATF Travel Rule information needs to go.

Where with a plain crypto asset address the VASP needs to ask all known counterparty VASP if they own this particular address, the Travel Address immediately makes this clear. Thus saving the user and the VASP time and effort. It also does not leak competitively sensitive information to counterparties.

Wallet Address

A wallet address is, in a way, like a bank account number in that it can be shared publicly without compromising the assets inside the wallet. Wallet addresses are what blockchains use to keep track of the allocation of assets and transaction chains.

Public/Private Key

For every virtual asset account created, a private key is generated, and this private key generates a public key which finally generates a Wallet Address. The private key should never be shared since it acts similarly to a pin code in traditional banking. The wallet address generated from the public key can be shared without compromising your funds.

GraphQL

We build our GUI on top of a GraphQL API. If you want to integrate 21 Travel Rule with your own IT infrastructure, you can use the same GraphQL API.

The API is self-documenting. The documentation linked is running on a server of ours with fake data and is periodically reset. No guarantees are made about uptime and stability. It can be used for exploring and testing only. You need to run our software on your IT infrastructure. Find the 'Docs' link in the upper left corner to view additional documentation.

Graphiql Docs

The graphiql docs

Authentication

The GraphQL API is authenticated with a secure cookie.

Creating a User

Creating a user happens with an HTTP POST request to the /users/login path. The request must include a Content-Type: application/json header. The JSON payload looks like this:

{
  "username": "some-username",
  "password": "some-password"
}

This request returns a cookie that is used to authenticate all other requests.

Authenticating Requests

Obtaining the cookie from the username/password combination happens in the same fashion as when creating a user. Just repeat the request used for creating a user. In order to authenticate GraphQL requests, include the returned cookie in the request. For example:

curl -H 'content-type: application/json' -b 'user=QgMwwh9+Q52CCuS0a6HAVr5BPSBY9nkgpRv5xU1DPYW1uhB1g2bd;' -d '{"query": "{trpd { vasps { name }}}"}' 'https://testing.21analytics.xyz/graphql'

Query

As the name suggests and as is the rule with GraphQL, use the operation type 'query' to return objects from the backend. There are several objects available:

  • autod: autod is the internal API that provides unified access to all databases and services of 21 Travel Rule. Its GraphQL API is used by the Web User Interface and custom integrated services from the VASP.

  • trpd: trpd is the processing engine for exchanging Travel Rule data through Travel Rule Protocol (TRP). It's possible to run multiple instances of trpd for HA, failover and zero-downtime updates.

  • aopd: aopd is the processing engine for registering ownership proofs through Address Ownership Proof Protocol (AOPP). It handles communication with self-hosted (non-custodial) wallets. It's possible to run multiple instances of aopd for HA, failover and zero-downtime updates.

  • email: email is a fallback mechanism for outgoing transactions to other VASPs. It connects to your SMTP server.

  • offline: offline is a fallback mechanism for incoming transactions from other VASPs. It allows you to upload, store and access visual proofs (screenshots and videos).

autod

The autod query groups all the different protocols. With it, you can query the backend for transactions, and the list of returned transactions contain a chronologically ordered list of transactions.

This is the query we recommend using when getting transactions from 21 Travel Rule.

txDetails(status, limit, offset)

This query returns a page of transactions independent of the protocol. For example, our 'Transactions' tab in our GUI uses this object exclusively. The status argument can be INBOX, WAITING or ARCHIVE.

Example

To return the amount from the TRP and email transactions in the inbox, run this:

{
  autod {
    txDetails(status: INBOX, limit:10 ,offset: 0) {
      ... on TrpdTx {
        trpd {
          amount
        }
      }
      ... on EmailTx {
        email {
          amount
        }
      }
    }
  }
}

txDetailsCount

This query returns the number of transactions with a particular status. Having this number is useful for pagination.

Example

{
  autod {
    txDetailsCount(status: INBOX)
  }
}

txAsXlsx(id)

As the name suggests, this returns a single transaction formatted as a XLSX string. Please note that the returned data is not a XLSX file but a GraphQL object where a field contains the XLSX data.

Example

{
  autod {
    txAsXlsx(id: "11353b43-7306-42cf-8b7d-15a1de4e9fc2")
  }
}

txsAsXlsx(status)

This returns all transactions formatted as a XLSX string. Please note that the returned data is not a XLSX file but a GraphQL object where a field contains the XLSX data.

Example

{
  autod {
    txsAsXlsx(status: INBOX)
  }
}

auditLogs(limit, offset)

Returns a list of event logs that have happened. Useful to view who has done what in the software.

Example

{
  autod {
    auditLogs(limit: 5, offset:0){
      username
      event
      target
      createdAt
    }
  }
}

auditLogsCount

Returns the total number of audit logs. This comes in handy when working with pagination.

Example

{
  autod {
    auditLogsCount
  }
}

trpd

This object contains all the TRP (Travel Rule Protocol) related data. All this data is already included in the autod query, and we suggest using that query instead.

vasps

The vasps object describes the registered counter-party, TRP enabled, VASPs. Only when a beneficiary LEI of an incoming TRP message matches the LEI of one of the registered VASPs does the autod txDetails object return the TRP message. This prevents spam.

Example

The following query returns the name and the optional public key for each configured counter-party VASP:

{
  trpd {
    vasps {
      name
      pubkey
    }
  }
}

See the live documentation for the entire list of queryable objects and fields.

configuration

As the name suggests, this object returns the current TRP configuration of your VASP.

Example

One of the configurable attributes is the LEI. Query the current value with:

{
  trpd {
    configuration {
      lei
    }
  }
}

validateTravelAddress(lnurl)

Validates the Travel Address and returns the beneficiary VASP. This will check if the counterparty VASP has sent a valid Travel Address. A Travel Address is valid if:

  • The counterparty VASP was whitelisted
  • The Travel Address encodes a URL
  • Checks if the url has a query parameter tag=travelRuleInquiry

Example

{
  trpd{
    validateTravelAddress(lnurl: "LNURL1DP68GURN8GHJ7CTSDYH8GETNW35KUEEWXGCKZMNPD3UHG6TRWVH8S7T69A68YCTWWDNX2UNN9UCXVD3EVCCNSVEDV5ERXCEDX33KYCEDVGCRWEFDXVCNVWRRVVUNJDNPVSUR7ARPVU7HGUNPWEJKC5N4D3J5JMN3W45HY7G6CNGTZ"){
      url
      name
      lei
      pubkey
      autoApproved
      createdAt
    }
  }
}

custodialUsers(limit, offset)

Allows you to get a list of users and their Travel Addresses. The lnurl is the field holding the Travel Address.

Example

{
  trpd{
    custodialUsers(limit:5, offset: 0){
      id
      firstName
      lastName
      asset
      walletAddress
      lnurl
      createdAt
    }
  }
}

txReport(id)

Creates a .docx transaction report, in base64-encoded form. This encoding is done for graphQL compatibility. You will then need to decode it.

Example

{
  trpd{
    txReport(id: "11353b43-7306-42cf-8b7d-15a1de4e9fc2")
  }
}

custodialUsersCount

Returns the number of entries in the custodial users table. You can use this number to paginate the users returned by custodialUsers(limit, offset).

Example

{
  trpd{
    custodialUsersCount
  }
}

aopd

Querying AOPP related objects happens here.

configuration

AOPD allows you to configure the callback URL the user's wallet should call with the generated proof and address. Query that URL with this object.

Example

{
  aopd {
    configuration {
      callbackUrl
    }
  }
}

proofs

When an end-user uses AOPP to create a verified withdrawal address, it creates a proof. Proofs currently come in three different formats: AopdSignatureProof, AopdMediaProof and AopdSatoshiProof. Once a proof is submitted, it is verified. Listing verified proofs happens with this object. Pagination is supported.

Example

Get the first ten proofs with:

{
  aopd {
    proofs(offset: 0, limit: 10) {
      ... on AopdSignatureProof{
        id
    	}
      ... on AopdMediaProof{
        id
    	}
      ... on AopdSatoshiProof{
        id
    	}
    }
  }
}

proofAsXlsx

As the name suggests, this returns a proof selected through its ID formatted as a XLSX file byte contents, encoded as base64 string. Please note that the returned data is not a XLSX file but a JSON document in which one field contains the XLSX data.

Example

{
  aopd {
    proofAsXlsx(id: "476e7b57-b661-4e34-afd3-bfcc8cc774f7")
  }
}

proofsAsXlsx

This returns all proofs formatted as a XLSX file byte contents, encoded as base64 string. Please note that the returned data is not a XLSX file but a JSON document in which one field contains the XLSX data.

Example

{
  aopd {
    proofsAsXlsx
  }
}

proofsCount

A supporting query that returns the number of verified proofs.

Example

{
  aopd {
    proofsCount
  }
}

email

Email is the fallback mechanism for staying compliant with the FATF Travel Rule.

configuration

Calling this query returns the email configuration of 21 Travel Rule.

Example

{
  email {
    configuration{
      smtpHost
      smtpPort
      smtpUsername
      smtpPassword
      smtpTlsSec
      fromEmailAddress
    }
  }
}

vasps

As with the trpd vasps query, this returns a list of registered VASPs to whom you can email.

Example

In essence, the same as the previously mentioned trpd vasps query.

{
  email {
    vasps {
      name
    }
  }
}

offline

Offline related queries.

txReport(id)

Creates a .docx transaction report, in base64-encoded form. This encoding is done for graphQL compatibility. You will then need to decode it.

Example

{
  offline{
    txReport(id: "784a1b47-a92b-4546-b5aa-e4fe5b2191d2")
  }
}

Mutation

As the name suggests and as is the rule with GraphQL, the operation type 'mutation' can be used to affect changes in the backend. Notice that the object type mutation is explicit in the examples. GraphQL allows omitting the query type but not the mutation type. There would be no way to distinguish them otherwise.

Similarly to queries, there are several objects available:

  • trpd: trpd is the processing engine for exchanging Travel Rule data through Travel Rule Protocol (TRP). It's possible to run multiple instances of trpd for HA, failover and zero-downtime updates.

  • aopd: aopd is the processing engine for registering ownership proofs through Address Ownership Proof Protocol (AOPP). It handles communication with self-hosted (non-custodial) wallets. It's possible to run multiple instances of aopd for HA, failover and zero-downtime updates.

  • email: email is a fallback mechanism for outgoing transactions to other VASPs. It connects to your SMTP server.

  • offline: offline is a fallback mechanism for incoming transactions from other VASPs. It allows you to upload, store and access visual proofs (screenshots and videos).

trpd

Everything related to the Travel Rule Protocol.

registerOrUpdateVasp

Before transactions are turned by the txDetails query, a VASP needs to be registered.

Example

mutation {
  trpd {
    registerOrUpdateVasp(vasp: {
        url: "https://someonesvasp.ch/",
        name: "CHANGES_HERE",
        lei: "ERTV00EW0P08QHUFXO73",
        autoApproved: false
    }) {
      name
    }
  }
}

updateConfiguration

In order to receive TRP messages your VASP needs to be configured. That is done with this query. Creating and updating are the same things. All arguments always need to be passed.

The lnurlBaseUrl is the external facing host to be used when exchanging messages in the advanced flow.

Example

mutation {
  trpd {
    updateConfiguration(config: {
      name: "Your VASP",
      lei: "RILFO74KP1CM8P6PCT96",
      lnurlBaseUrl: "https://api.testing.21analytics.xyz/",
      matchNameIgnoreCase: false,
      matchNameLevenshteinThreshold: 0
    }) {
      name
    }
  }
}

aopd

Changing AOPP related server state happens under this object type.

updateConfiguration

The AOPP functionality of 21 Travel Rule knows only one configuration parameter. This parameter is called callbackUrl and governs where a user's wallet should POST the proof when completing an AOPP request.

Example

mutation {
  aopd {
    updateConfiguration(config: {callbackUrl: "https://api.testing.21analytics.xyz/proofs/"}) {
      callbackUrl
    }
  }
}

registerProof

Proofs obtained via wallets that do not support AOPP can be stored with this query. For example, the end-user has manually submitted the signature for a signing challenge.

Example

mutation {
  aopd {
    registerProof(proof: {
      message: "hello", 
      asset: BTC, 
      address: "bc1qnshsvhrfl28g03k0vxdez6vua56r0c72xy9e93", 
      signature: "H1oYVmDaWxZBPEk2ou4myn1SRC20ycBUPPD5fLS+SmQ1e04Bi1J9mIJ5fNhe3khDhJRUX2fU+VHGKlJdAjYIvBU="
    }) {
      id
    }
  }
}

email

Email is the fallback mechanism that when all else fails, allows you to remain compliant.

updateConfiguration

Here you can configure your VASP's email server to send outgoing emails.

Example

mutation {
  email {
    updateConfiguration(config:{
      smtpHost: "some host"
      smtpPort: 25
      smtpUsername: "some smtp user"
      smtpPassword: "some smtp password"
      smtpTlsSec: TLS
      fromEmailAddress: "from@my-vasp.com"
      notificationEmailAddress: "notification@my-vasp.com"
    }) {
      smtpHost      
    }
  }
}

registerVasp

Sending emails can only be sent to previously registered VASPs. Use this query to register email VASPs.

Example

mutation {
  email {
    registerVasp(vasp: {
      name: "Your Vasp"
      email: "changes_here@my-vasp.com"
    }) {
      email
    }
  }
}

distrustVasp

When the email option to a VASP is no longer required, use this query to remove it from the list. Previously sent transactions are retained.

Example

mutation {
  email {
    distrustVasp(id:"96e2d54c-4680-48be-ac17-30fb6aa17ae6") {
      id
    }
  }
}

registerTx

The same consideration hold here as with the registerTx mutation under the trpd object. Again, either addressLine OR street and number are required.

Example

mutation {
  email {
    registerTx(txDetails: {
      asset:BTC
      amount:"1230"
      txId:"68cf8b54b8af3e81c7d6186d20aada6a8896de4c9329b79427950b84334262ff"
      originator:{
          firstName:"some first name"
          lastName:"some last name"
        address:{
          country:"ch"
          town:"zug"
          postalCode: "1111"
          addressLine:"Dorpstraat 1"
        }
        customerIdentification:"some id"
      }
      beneficiary: {
          firstName:"some first name"
          lastName:"some last name"
        walletAddress:"123"
      }
      beneficiaryVaspId:"123"
    })
  }
}

offline

Sometimes Travel Rule information is received out of band. 21 Travel Rule allows entering that information in the system nonetheless. Doing so makes sure that Travel Rule compliance data is held at a single source of truth.

registerTx

The mutation is very similar to the other registerTx mutations. The notable difference is that there's a field called originatorVasp. Because this Travel Rule data was received out of band, the originator VASP is to be specified. To this end, the name field is required and the lei field is optional.

Example

mutation {
  offline {
    registerTx(txDetails: {
      asset:BTC
      amount:"1230"
      txId:"68cf8b54b8af3e81c7d6186d20aada6a8896de4c9329b79427950b84334262ff"
      originator:{
          firstName:"some first name"
          lastName:"some last name"
        address:{
          country:"ch"
          town:"zug"
          postalCode: "1111"
          addressLine:"Dorpstraat 1"
        }
        customerIdentification:"some id"
      }
      beneficiary: {
          firstName:"some first name"
          lastName:"some last name"
        walletAddress:"123"
      }
      originatorVasp: {
        name: "some name"
      }
    })
  }
}

Subscriptions

The operation type 'subscriptions' is used to get pushed updates from the server when certain data changes. Notice that the object type subscription is explicit in the examples. GraphQL allows omitting the query type but not the subscription type. There would be no way to distinguish them otherwise.

There are currently two subscriptions available:

aopdAoppProofs

This subscription listens to AOPP proofs and will receive a notification with the full signature proof.

Unfortunately, subscriptions are not supported on GraphiQL so no examples can be provided.

trpdTransactons

This subscription is mainly used to listen for changes in 'status'. That is changes from any of the three statuses; INBOX, WAITING, or ARCHIVED.

Unfortunately, subscriptions are not supported on GraphiQL so no examples can be provided.

GUI

Compliance officers are the targeted users of the GUI of 21 Travel Rule. In their day-to-day operations, they will use the GUI to send out and review incoming Travel Rule messages. The GUI also displays AOPP proofs for the compliance officer to inspect and export.

We expect low volume (but compliant!) customers to use the GUI. High volume customers might find it helpful as a dashboard of sorts.

Handling the management of known VASPs and the configuration of your VASP happens in the GUI as well.

In summary, the GUI exposes the following functionality:

  1. Sending of Travel Rule messages
  2. Reviewing of incoming Travel Rule messages
  3. Own VASP configuration
  4. Known counterparty VASP configuration
  5. AOPP proof inspection
  6. Export of data

Sign Up

Once 21 Travel Rule is installed and used for the first time, the user is prompted to set a password and, optionally, configure counterparty VASPs.

User Name and Password

When the database doesn't contain a user, 21 Travel Rule prompts the compliance officer to create one.

Initial user creation

Initial user creation

You can un-hide the password fields. There are no password restrictions defined. Only one user is currently supported in the GUI (the API supports more). After entering the required user name and password, you are logged in immediately. The user name shows in the upper right corner of 21 Travel Rule.

Configure Your VASP

Once logged in, you need to configure your own VASP. You can configure four different sets of attributes:

  1. TRP
  2. Email
  3. Address Ownership
  4. Other Protocols

Fill out those you feel are relevant to your VASP.

You can find these configuration settings in the dropdown menu to the right of the header.

Configure your VASP dropdown

Configure your VASP dropdown.

Once you have saved a configuration, you are encouraged to configure counterparty VASPs.

TRP

TRP is our preferred protocol. First, fill out the name of your VASP and the associated LEI. Upon saving, a Signing Public Key shows. If you wish to use message signing, you can give this to a counterparty VASP.

Own VASP configuration

Configure your VASP

The VASP Legal Name field is used to identify the VASP. For example, 'SDX'.

VASP LEI

The LEI (Legal Entity Identifier) field is a unique identifier for your VASP, you can register for this on the Global Legal Entity Identifier Foundation (GLEIF) website or, for testing purposes, use our LEI Generator. The owner of the VASP or a representative should present you with their LEI; however, the LEI registry is also public.

Base URL to Construct Travel Addresses

The URL your VASP decides to use to construct a Travel Address. One example of this would be https://api.bitcoin-exchange.ch. This should be provided by your IT team.

Signing Public Key

This key is generated once the TRP configuration data has been submitted. It is part of a public/private key that is randomly generated. The purpose of this is to add an additional layer of security by using virtual signatures to authenticate TRP messages.

Chainalysis API Key (optional)

When a Chainalysis API Key is added then all the transaction ID's and cryptocurrency addresses are submitted to chainalysis for risk analysis. You can contact your technical team to request the Chainalysis API Key.

Beneficiary Name Matching

Next, choose how strict or lenient you want to be regarding beneficiary name matching. Opt to ignore the letter case altogether or allow up to 2 mistakes in the spelling or case size. This flexibility will work on the counterparty VASP sending a transaction.

Name Matching

Fuzzy Name Matching

Enabling this option allows you to be lenient with names submitted to you. It has no effect on names you submit to others.

Ignore Case

The ignore case will allow the counterparty VASP sending the transaction to do this even if the beneficiary name matching the Travel Address cases doesn't match.

Name Mismatches Allowed

Name mismatch allows your counterparty to be more lenient with the names associated to one of your users' Travel Address. You can choose between one or two mistakes. Mistakes are counted using Levenshtein distance.

For example:

'cat' and 'cot' has distance 1 'cat' and 'calm' has distance 2

You can test it here.

Email

If a counterparty VASP does not support any Travel Rule protocol, 21 Travel Rule can send an email with the relevant information instead. This makes you compliant with the Travel Rule nonetheless.

If you want to configure email, and we recommend you do, you can do that on the 'Email' tab. If you are unsure of this information, consult your IT team to complete the fields.

Configure your VASP Email

Configure your VASP: Email

SMTP Host

An SMTP server is used to send and receive emails. Your SMTP server is either managed by your IT department or an external company. Gmail is an example of a host. If you are unsure of this information, it is best to check with your IT team.

"From" Email Address

This is the email address you will use to send information from.

SMTP Port Number (optional)

This is the port number of your SMTP. The standard secure SMTP port is 587. As a common alternative, port 2525 is usually used. With this said, this field is optional and defaults to 587; if you are unsure, leave this empty.

SMTP Username (optional)

This is the name of the user on the SMTP server you want to use. This will usually be configured on the SMTP server by your IT team.

SMTP Password (optional)

Once your IT team configures the SMTP username in the SMTP server configuration, a password will also be required.

Select Connection Security (optional)

These can be one of three options:

  • STARTTLS
  • TLS
  • OPTIONAL STARTTLS

The default value we use is usually STARTTLS, although our software will automatically choose the option more appropriate for you. If issues do appear you can consult your IT team.

Address Ownership

On the tab 'Address Ownership', you can configure the AOPP callback URL and Satoshi Test base amounts.

Configure your VASP Address Ownership

Configure your VASP: Address Ownership

AOPP Callback URL

The callback URL where address proofs can be submitted. The end user's wallet uses this URL to submit a signature and new virtual asset address. This URL must be publicly accessible. Ensure that this URL points to where the backend is running; your IT team will be able to provide this information for you.

Satoshi Test Base Amounts

Randomised amounts will be generated for each Satoshi Test based on the amounts input below. It is a best practice to set them to a fiat equivalent of about $1.

Other Protocols

Our software offers to configure any protocol upon request. For this, one must contact the 21 Analytics team and request access. Some additional recommendations besides our prefered TRP and AOPP protocols are the following:

  • VerifyVasp

Counterparty VASPs

Each incoming transaction needs to be associated with a counterparty VASP. If the incoming transaction's counterparty VASP is not configured, you will not see that transaction in the Inbox.

Each counterparty VASP supports exactly one protocol. That protocol can be:

Add VASP

You can add a counterparty VASP to interact with by clicking on the 'Add VASP' button on the top right of the 'Counterparty VASP' page.

Add Vasp Button

This will prompt a modal on which you can select which protocol you will use to interact with the counterparty VASP.

Add TRP VASP

Under the TRP tab, we see the following input data fields:

Add Vasp TRP

The registered legal name associated with the LEI of the counterparty VASP. This should be provided by the counterparty VASP you wish to operate with.

VASP LEI

The VASP LEI (Legal Entity Identifier) field is the unique identifier of your counterparty VASP. The LEI registry is public, so you can search the counterparty you want to operate with in the Global Legal Entity Identifier Foundation (GLEIF) website or directly communicate with the counterparty VASP to request this information.

URL

The URL your counterparty VASP decides to use to construct Travel Address. One example of this would be https://api.counterparty-vasp.ch. Your counterparty VASP should provide this.

Signing Public Key (optional)

This field adds a layer of security by using public/private keys to encrypt TRP messages. The counterparty VASP should provide this Public Signing Key. You must ensure your counterparty has your Public Signing Key field added for this to work. You can find this in the Configure your VASP page under the TRP tab and share it with the counterparty VASP that you wish to establish this extra layer of security.

Approve Transactions from This VASP Automatically

This checkbox input allows you to activate auto-approval. This means the whole TRP flow process will automatically approve any transaction coming from the particular counterparty VASP being configured.

Add VASP Email

As a fallback method, you can use Email; this is very simple to set up but comes with numerous drawbacks. Under the Email tab, we see the following input data fields:

Add Vasp Email

Name

The name of the counterparty VASP the email is associated with.

Email

The email address of the counterparty VASP you wish to operate with. One example of this would be compliance@counterpartyvasp.ch.

Other Protocols

Upon request, other protocols can be easily configured.

TRP

On the Counterparty VASP page you can find the TRP tab.

Counterparty VASP TRP Header

TRP tab

Here you will find a table with a list of vasps your team added choosing the TRP protocol.

Counterparty VASP TRP Table

This table contains the data introduced by you or someone with access to the account. To add additional VASPs you should use the Add VASP button stated previously and choose the TRP tab. This table has the following columns:

Name

List of the chosen VASP names associated with the LEI (Legal Entity Identifier).

API URL

The list of URLs associated with the LEI (Legal Entity Identifier). This is required to establish a connection through TRP (Travel Rule Protocol).

LEI

A list of LEIs (Legal Entity Identifiers). This is provided by the counterparty VASP when it is added to the table.

Optional Signing Key

The list of signing keys provided by the counterparty VASP. This field is optional; if your team decides not to use it, the text will be disabled in this field. Ensure your IT team has also provided your own Public Signing Key to the counterparty VASP for communication to work correctly with additional security.

Auto Approve

This field is indicated if the auto-approve feature is enabled. You will see either a tick or a cross within a box.

Counterparty VASP TRP Autoapprove tick

If you see a tick, it has indeed been activated, and all the steps in the TRP flow will trust and accept all transactions that come from the associated counterparty VASP.

Counterparty VASP TRP Autoapprove cross

If you see the cross, it's disabled, and transactions will need to be approved or declined by you manually. This adds a layer of control and filtering if desired.

Actions

Here you can find the Edit button.

Counterparty VASP Edit Button

Clicking on this button allows you to change all the fields in the row except the LEI. The LEI works as the ID associated with all the previous transactions linked to this VASP; this can not be changed.

Counterparty VASP TRP Editing

Once changes have been made you will now see two options under the actions column. These are Save VASP:

Counterparty VASP Edit Save VASP

or Cancel:

Counterparty VASP Edit Cancel

which reverts all changes made.

Email

On the Counterparty VASP page, you can find the Email tab.

Counterparty VASP Email

Email tab

Here you will find a table with a list of VASPs your team added choosing the Email protocol.

Counterparty VASP Table

This table contains the data introduced by you or someone with access to the account. To add additional VASPs you should use the Add VASP button stated previously and choose the Email tab. This table has the following columns:

Name

List of the chosen VASP names.

Email

The email associated with the VASP name. This must be correct to establish communication between the counterparty VASP.

Actions

There are two actions available under the Email tab:

Edit

By clicking on the Edit button,

Counterparty VASP Edit Button

you will be able to edit the row and change the email of the VASP. This is in case the VASP changes its email.

Counterparty VASP Email Editing

Once changes have been made, you will see two options under the actions column. These are Save VASP:

Counterparty VASP Edit Save VASP

or Cancel:

Counterparty VASP Edit Cancel

which reverts all changes made.

Distrust

This option allows you to sever further communication with the counterparty VASP by blocking emails. You simply need to click on the Distrust button.

Counterparty VASP Distrust

This will be reflected in the row associated with the VASP in the GUI.

Counterparty VASP Distrust

Other Protocols

Additional tabs can be added to include other protocols.

Counterparty VASP Other Protocols Header

VerifyVASP example

To do this please chat to the 21 Analytics team. The tables will need to be configured depending on the chosen protocol.

Address Book

Under the Address Book tab in the header, you will find the Address Book page. This page allows you to have all your customers organised in one place and easily create Travel Addresses for them. It is recommended that this process be automated through the API or by preparing a script to integrate with your custodial system if you have one. The GUI is intended for low-volume customers. There are two main parts on this page:

Register A Customer

Note: We recommend that you automate this manual proccess using the API.

The Register A Custom button allows you to add a new customer to the table.

Address Book Register A Customer Button

This will prompt a modal with the information required to register the customer.

Address Book Register A Customer Button

Here you can find the following input fields:

Customer Account ID (optional)

If your VASP already has a custodial system in place, you can use this field to keep the IDs the same. If left empty, our software will automatically generate an ID for your customer.

First Name

The legal first name of the customer you want to register.

Last Name

The legal last name of the customer you want to register.

Wallet Address

The crypto asset wallet address of the customer you want to register. An example of this could be bc1q76lt4wgz4kr30mq3e9gayzgnmd0hy74v8ekrla for bitcoin or 0xcBfa884044546d5569E2abFf3fB429301b61562A for ethereum.

Virtual Asset

Here you can find a dropdown with all the crypto assets we support. You should choose the asset associated with the customer's wallet address. If we do not yet support a crypto asset you need, you can always contact the 21 Analytics team and request it.

Customers Overview

The customer table shows all the listed customers that were registered through the Register a Customer button.

Address Book Customers Overview

In this table, you can find the following columns:

ID

Here you can find the list of IDs associated with the registered customers. Our software might have created this if the field had been left empty. If you or your team added it, it is most likely an ID associated with your current custodial system.

Name

A list of the first and last name of your customers.

Virtual Asset

A list of the virtual assets associated with the customers’ wallet addresses.

Wallet Address

This column shows a list of the wallet addresses used by your customers to generate the Travel Address.

Travel Address

A list of Travel Addresses automatically generated for your customers. This allows you to easily use the TRP protocol. You can use this when Sending a Transaction Notification.

Transactions

Our Transactions page consists of three tabs:

Inbox

Transactions Inbox Table

Transactions Inbox Tab


Waiting

Transactions Waiting Tab

Transactions Waiting Tab


Archived

Transactions Archived Tab

Transactions Archived Tab


To understand this page it is recommended to have a basic understanding of the TRP flow.

Transactions Inbox

Our Transactions Inbox panel is divided into five distinct elements.

Send Transaction Notification

Send Transaction Notification Button

Transactions Overview

Transactions Inbox Table

Detailed Transaction

Transactions Inbox Details

Export Transactions

Export Transactions Button

Pagination

Transactions Pagination

Send Transaction Notification

Note: We recommend that you automate this manual proccess using the API.

You can send a transaction manually to a counterparty VASP using the Send Transaction Notification button.

Send Transaction Notification Button

This will prompt a three-stage form modal consisting of:

1. Beneficiary Information

The Beneficiary Information step of the form will show two options depending on the protocol the counterparty VASP uses.

TRP

If a valid Travel Address is provided by your counterparty VASP this means you can use the TRP protocol. This currently comes in the form of an LNURL.

Send Transaction Notification Beneficiary TRP

First Name

If a Travel Address is used, the first name must be the one associated with the LNURL Travel Address.

Last Name

If a Travel Address is used, the last name must be the one associated with the LNURL Travel Address.

Travel Address

The Counterparty VASP must provide the Travel Address. This LNURL contains information about the customer of the Counterparty VASP that wishes to do the transaction. If this is introduced correctly, our software will infer what counterparty VASP data the LNURL is associated with.

Email

If a VASP does not have TRP as a fallback, email can be used. To use this protocol, you can simply add a Wallet Address instead of a Travel Address.

Send Transaction Notification Beneficiary Email

First Name

The first name of the customer who owns the Wallet Address.

Last Name

The last name of the customer who owns the Wallet Address.

Wallet Address

A Wallet Address with the correct format for any of our supported virtual assets.

2. Virtual Asset

This step will show different fields depending on your chosen protocol in the Beneficiary Information step. You will see the following fields on both flows:

Virtual Asset

Here you can find a list of all the assets we support. If you can't find an asset, you can always contact 21 Analytics and request to add an asset. You should choose the virtual asset used in the transaction.

Amount

The amount of the virtual asset selected for the transaction.

TRP

If you are using the TRP protocol, you will now see the second step of the form:

Send Transaction Notification Virtual Asset TRP

Email

If you are using the Email protocol, you will now see the second step of the form:

Send Transaction Notification Virtual Asset Email

This has some additional fields apart from Virtual Asset and Amount; these are:

Beneficiary VASP

A list of VASPs added on Counterparty VASPs. You should choose the VASP the beneficiary is a customer of.

Transaction ID (TxID)

A virtual asset transaction will create a Transaction ID for this particular transaction in the blockchain. You can usually find these either on your wallet or on a blockchain explorer.

3. Originator Information

The third and last step of the form is the Originator Information.

Send Transaction Notification Originator

Here you will find the following steps:

First Name

The first name of the originator customer of your VASP.

Last Name

The last name of the originator customer of your VASP.

Internal Account Number

A number used to identify your customer within your system. This can be mapped to an external custodial system. Each customer should have a unique Account Number.

Street Name (optional)

The name of the street the originator customer of your VASP resides at on their ID. This is optional unless you are using TRP. If TRP is used, either this field or Address Line is required, as well as the Building Number.

Building Number (optional)

The building number on either the street or address in which the originator customer of your VASP resides at on their ID. This is optional unless using the TRP flow. This would be required alongside either the Steet Name or Address Line fields.

Address Line (optional)

The Address Line where the originator customer of your VASP resides, as shown on their passport or ID. This is optional unless using the TRP flow. If TRP is used, either this field or Street Name is required, as well as the Building Number.

Postal Code

The postal code of the originator customer of your VASP. This postal code is tied to the address on the customer's passport or national ID.

City

City where the originator customer resides.

Country

Country where the originator customer resides.

Transactions Inbox Overview

Our transactions inbox overview lets you easily navigate through all transactions that require an action from your side.

Transactions Inbox Table

Transactions Inbox Overview Table


Let's now do a breakdown of the tables row.

Transactions Inbox Table

Transactions Inbox Overview Table Row


As you can see, this row contains the following fields:

Type
Status
Beneficiary
Originator
Originator VASP
Virtual Asset
Amount
Date Created
Actions

Type

Can be either Sent or Received. Hovering over the icon will reveal a tooltip with the type of transaction.

Transaction Type Received

When the Type has the Received value, represented by the box logo above, a counterparty VASP has sent you, the compliance officer, a transaction. This transaction is then waiting for you to Approve it or Decline it.

Transaction Type Sent

When the Type has the Sent value, it means you have sent a transaction to a counterparty VASP through the Send Transactions Notifications button, and the counterparty VASP has then approved or declined the transaction. This is then waiting for the last step, which is for you to Confirm or Cancel the transaction.

For a better understanding of the whole workflow, check: Advanced Workflow.

Status

Can be either Pending or Approved.

Transactions Notifications Pending

Once a counterparty VASP has sent a transaction to you, the transaction in your inbox will go into a Pending status until you Approve or Decline the transaction. The status of Pending is of Type Received when it is in the inbox.

Transaction Status Apprvoed

If, on the other hand, you have sent a transaction to a counterparty VASP through the button and the counterparty VASP has clicked , the Status of will appear in your inbox for that particular transaction. The Status of Approved is of when it's in the inbox.

If, on the other hand, you have sent a transaction to a counterparty VASP through the Send Transactions Notification button and the counterparty VASP has clicked Approve, the Status of Approved will appear in your inbox for that particular transaction. The Status of Approved is of Type Sent when it's in the inbox.

Beneficiary

The name and surname, as registered in a legal identity document of the person receiving the transaction.

Originator

The name and surname, as registered in a legal identity document of the person sending the transaction.

Originator VASP

The VASP of the customer who sent the transaction.

Virtual Asset

Transactions Virtual Asset

Shows the symbol, name and currency code of the Virtual Asset Sent or Received in the transaction. Clicking on the name will redirect you to coinmarketcap, where you can see live information about the transaction’s asset.

Amount

The amount of a particular Virtual Asset sent from the Originator to the Beneficiary. Yes, we do support the entire 18 decimals for Ethereum.

Date Created

The time and date the transaction was Sent or Received. The format shown is day/month/year and hour/minutes/seconds. The time will be shown in the time zone configured in your browser and can be seen by hovering over the transaction date. This will reveal a tooltip with the timezone: continent/city format.

Actions

Transaction Actions

You will find a “tick” and “cross” sign under the actions column. Moving the cursor over the icons will show “approve” and “cancel” on a tooltip box.

If you click approve, a modal will pop up requesting the user to introduce the transaction ID of the transaction in hand. This can usually be found in your wallet when doing a transaction. It can also be found in a blockchain explorer or through a command line tool for more technical users. Once the transaction has been confirmed by clicking the “confirm” button, the row will disappear from the inbox and move to the archived table.

If the user presses the cancel option, a different modal will appear, allowing the user to optionally add a comment as to why he has decided to cancel the transaction. Once the “cancel transaction” button has been clicked, the row will disappear from the inbox and move to the archived table.

Detailed Transaction

When you click on one of the rows in the Transactions Overview table, a popup modal will display.

Transactions Inbox Details

Travel Transaction ID

At the top left of the modal, you can find a title. This title shows the ID of the transaction in our software. This should not be confused with the blockchain transaction ID. This is used to find specific transactions from all your list of transactions.

Transaction Status

Can be either Pending or Approved.

Transactions Notifications Pending

Once a counterparty VASP has sent a transaction to you, the transaction in your inbox will go into a Pending status until you Approve or Decline the transaction. The status of Pending is of Type Received when it is in the inbox.

Transaction Status Approved

If, on the other hand, a counterparty VASP has sent you a transaction and you have approved it, the Status of Approved will appear in your waiting table for that particular transaction. The Status of Approved is of Type Recieved when it's in the waiting table.

Transaction Type

Can be either Sent or Received.

Transaction Type Received

When the Type has the Received value, it means a counterparty VASP has sent you, the compliance officer, a transaction. This transaction is then waiting for you to Approve it or Decline it.

Transaction Type Sent

When the Type has the Sent value, it means you have sent a transaction to a counterparty VASP through the Send Transactions Notifications button, and the counterparty VASP has then approved or declined the transaction. This is then waiting for the last step, which is for you to Confirm or Cancel the transaction.

For a better understanding of the whole workflow, check: TRP.

Protocol

The protocol used for the transaction to be compliant.

Date Created

The time and date the transaction was Sent or Received. The format shown is day/month/year and hour/minutes/seconds. The time will be shown in the time zone configured in your browser and can be seen by hovering over the transaction date. This will reveal a tooltip with the timezone: continent/city format.

Virtual Asset

Transaction Virtual Asset

Shows the symbol, name and currency code of the Virtual Asset Sent or Received in the transaction. Clicking on the name will redirect you to coinmarketcap, where you can see live information about the transaction’s asset.

Amount

Transactions Amount

The amount of a particular Virtual Asset sent from the Originator to the Beneficiary. Yes, we do support the entire 18 decimals for Ethereum. You can also find the equivalent of the chosen Virtual Asset as a US dollar equivalent. This is fetched from messari.com, and if you hover over the USD value, you will see the date and time when the inquiry was converted.

Beneficiary Name

The first and last name of the customer who is receiving the virtual asset.

Beneficiary VASP Name

The name of the VASP of the customer receiving the virtual asset. This name is associated with a LEI.

Beneficiary VASP LEI

The LEI (Legal Entity Identifier) of the VASP who is receiving the virtual asset.

Beneficiary Wallet Address

The Wallet Address of the customer receiving the virtual asset of the transaction.

Originator Name

The first and last name of the customer sending the virtual asset.

Originator VASP Name

The name of the VASP of the customer sending the virtual asset. This name is associated with a LEI.

Originator VASP LEI

The LEI (Legal Entity Identifier) of the VASP who is sending the virtual asset.

Originator Internal Account Number

An account number to identify the customer sending the transaction. Each customer should have their own Account Number.

Originator Address

The Address where the customer resides. This can be found on their passport or country ID.

Actions

Can be either Approve or Decline if the transaction was received by a counterparty VASP.

Approve Decline Buttons

Approve

If approved, this will be sent to the counterparty VASP to finish the verification of the transaction.

Decline

If declined, the transaction won't go through.


If the transaction is sent by you and then approved by a counterparty VASP, there will be two actions: Confirm or Cancel.

Confirm Cancel Buttons

Confirm

If the transaction was previously approved by the counterparty VASP, this button will confirm the transaction and finish the verification process.

Cancel

If the transaction was previously approved by the counterparty VASP, this button will cancel the process, and the transaction won't go through.

Export

Export Button

This will download an .xlsx spreadsheet with the data of this particular transaction. This can later be imported to any spreadsheet software for further manipulation of data.

Download Report

Download Report Button

This will download a .doc file with a written report template of the transaction. You can manipulate this on text editing software.

Export Transactions

Export Transactions Button

The Export Transaction button allows you to export all the transactions in the inbox as an .xlsx file. This can then be imported to a spreadsheet software to manipulate further if needed, which is handy for filtering and ordering data in a specific way. It also allows you to search for a specific entry. The 21 Analytics team prioritised the export functionality since it allows compliance officers to use software they are familiar with and allows for an additional storage method. Please contact our team if you have any extra functionality requirements.

Pagination

Once enough transactions have been populated, pagination appears at the bottom left, under the table.

Transactions Pagination

A new page will be created when 15 rows have been populated on the overview table. You can navigate these by either clicking on the page number or going to the next or previous pages using the arrows.

Note: Our screenshots currently display 13 rows. This was done to show extra elements like the Export Transactions button. The software will display 15, as mentioned.

Transactions Waiting

Our Transactions Waiting panel is divided into four distinct elements.

Transactions Overview

Transactions Waiting Table

Detailed Transaction

Transactions Waiting Details

Export Transactions

Export Transactions Button

Pagination

Transactions Pagination

Transactions Waiting Overview

Our transactions waiting overview lets you easily navigate through all transactions requiring an action from the counterparty VASP you are communicating with.

Transactions Waiting Table

Transactions Waiting Overview Table


Let's now do a breakdown of the tables row.

Transactions Waiting Table

Transactions Waiting Overview Table Row


As you can see this row contains the following fields:

Type
Status
Beneficiary
Originator
Originator VASP
Virtual Asset
Amount
Date Created
Counterparty Action

Type

Can be either Sent or Received. Hovering over the icon will reveal a tooltip with the type of transaction.

Transaction Type Received

When the Type has the Received value, represented by the box logo above, a counterparty VASP has sent you, the compliance officer, a transaction. You then approved this transaction and are now waiting for the counterparty VASP to either Confirm or Cancel.



Transaction Type Sent

When the Type has the Sent value, it means you have sent a transaction to a counterparty VASP through the Send Transactions Notifications button, and the counterparty VASP and are now waiting for the counterparty VASP to either Approve or Decline the transaction.

For a better understanding of the whole workflow, check: Advanced Workflow.

Status

Can be either Pending or Approved.

Transactions Notifications Pending

Once you have sent a transaction to a counterparty VASP, the waiting transaction will go into a Pending status until the counterparty VASP Approves or Declines the transaction. The status of Pending is of Type Sent when it's waiting.

Transaction Status Apprvoed

If you have received a transaction from a counterparty VASP and then Approved, the Status of Approved will appear, and the transaction will be in a waiting state until the counterparty VASP either Confirms or Cancels the transaction. The Status of Approved is of Type Recieved when it's waiting.

Beneficiary

The name and surname, as registered in a legal identity document of the person receiving the transaction.

Originator

The name and surname, as registered in a legal identity document of the person sending the transaction.

Originator VASP

The VASP of the customer who sent the transaction.

Virtual Asset

Transactions Virtual Asset

Shows the symbol, name and currency code of the Virtual Asset Sent or Received in the transaction. Clicking on the name will redirect you to coinmarketcap, where you can see live information about the transaction's asset.

Amount

The amount of a particular Virtual Asset sent from the Originator to the Beneficiary. Yes, we do support the entire 18 decimals for Ethereum.

Date Created

The time and date the transaction was Sent or Received. The format shown is day/month/year and hour/minutes/seconds. The time will be shown in the time zone configured in your browser and can be seen by hovering over the transaction date. This will reveal a tooltip with the timezone: continent/city format.

Counterparty Action

Can be either Requires Approval or Requires Confirmation.

If Requires Approval shows, that means you sent a transaction and the transaction is waiting for the counterparty VASP to either Approve or Decline the transaction.

If, on the other hand, Requires Confirmation appears on this field that means that the counterparty VASP sent you a transaction and you then approved it. Now the transaction is waiting for the last step which is for the counterparty VASP to either Confirm or Cancel the transaction.

Detailed Transaction

A popup modal will display when you click on one of the rows in the Transactions Overview table.

Transactions Inbox Details

Travel Transaction ID

At the top left of the modal, you can find a title. This title shows the ID of the transaction in our software. This should not be confused with the blockchain transaction ID. This is used to find specific transactions from all your list of transactions.

Transaction Status

Can be either Pending or Approved.

Transactions Notifications Pending

Once you have sent a transaction to a counterparty VASP, the transaction will go into a Pending status on your waiting table until it gets Approved or Declined by the counterparty VASP. The status of Pending is of Type Sent when it's waiting.



Transaction Status Approved

If you have sent a transaction to a counterparty VASP through the Send Transactions Notification button and the counterparty VASP has clicked Approve, the Status of Approved will appear in your inbox for that particular transaction. The Status of Approved is of Type Sent when it's in the inbox.

Transaction Type

Can be either Sent or Received.

Transaction Type Received

When the Type has the Received value, it means a counterparty VASP has sent you, the compliance officer, a transaction, you have then approved this and its waiting for the last step which is for the counterparty VASP to either Confirm or Cancel the transaction.

Transaction Type Sent

When the Type has the Sent value, it means you have sent a transaction to a counterparty VASP, and the counterparty VASP has to now either Approve or Decline the transaction. Until then the transaction will be on the waiting table.

For a better understanding of the whole workflow, check: TRP.

Protocol

The protocol used for the transaction to be compliant.

Date Created

The time and date the transaction was Sent or Received. The format shown is day/month/year and hour/minutes/seconds. The time will be shown in the time zone configured in your browser and can be seen by hovering over the transaction date. This will reveal a tooltip with the timezone: continent/city format.

Virtual Asset

Transaction Virtual Asset

Shows the symbol, name and currency code of the Virtual Asset Sent or Received in the transaction. Clicking on the name will redirect you to coinmarketcap, where you can see live information about the transaction’s asset.

Amount

Transactions Amount

The amount of a particular Virtual Asset sent from the Originator to the Beneficiary. Yes, we do support the entire 18 decimals for Ethereum. You can also find the equivalent of the chosen Virtual Asset as a US dollar equivalent. This is fetched from messari.com, and if you hover over the USD value, you will see the date and time when the inquiry was converted.

Beneficiary Name

The first and last name of the customer who is receiving the virtual asset.

Beneficiary VASP Name

The name of the VASP of the customer receiving the virtual asset. This name is associated with a LEI.

Beneficiary VASP LEI

The LEI (Legal Entity Identifier) of the VASP who is receiving the virtual asset.

Beneficiary Wallet Address

The Wallet Address of the customer receiving the virtual asset of the transaction.

Originator Name

The first and last name of the customer sending the virtual asset.

Originator VASP Name

The name of the VASP of the customer sending the virtual asset. This name is associated with a LEI.

Originator VASP LEI

The LEI (Legal Entity Identifier) of the VASP who is sending the virtual asset.

Originator Internal Account Number

An account number to identify the customer sending the transaction. Each customer should have their own Account Number.

Originator Address

The Address where the customer sending the transaction resides. This can be found on their passport or country ID.

Export

Export Button

This will download an .xlsx spreadsheet with the data of this particular transaction. This can later be imported to any spreadsheet software for further manipulation of data.

Download Report

Download Report Button

This will download a .doc file with a written report template of the transaction. You can manipulate this on text editing software.

Export Transactions

Export Transactions Button

The Export Transaction button allows you to export all the transactions in the waiting table as an .xlsx file. This can then be imported to a spreadsheet software to manipulate further if needed, which is handy for filtering and ordering data in a specific way. It also allows you to search for a specific entry. The 21 Analytics team prioritised the export functionality since it allows compliance officers to use software they are familiar with and allows for an additional storage method. Please contact our team if you have any extra functionality requirements.

Pagination

Once enough transactions have been populated, pagination appears at the bottom left, under the table.

Transactions Pagination

A new page will be created when 15 rows have been populated on the overview table. You can navigate these by either clicking on the page number or going to the next or previous pages using the arrows.

Note: Our screenshots currently display 13 rows. This was done to show extra elements like the Export Transactions button. The software will display 15, as mentioned.

Archived Transactions

Our Archived Transactions panel is divided into five distinct elements.

Archive New Transaction

Archive New Transaction

Transactions Overview

Archived Transactions Table

Detailed Transaction

Archived Transactions Details

Export Transactions

Export Transactions Button

Pagination

Transactions Pagination

Archive New Transaction

Note: We recommend that you automate this manual proccess using the API.

To manually archive a transaction you can use the 'Archive New Transaction' button. This can be done offline since it doesn't need to communicate with the counterparty VASP.

Archive New Transaction

Once you click the button a three-stage form will appear:

1. Beneficiary Information

Similar to the Send Transaction Notification button we saw in the Inbox tab, the first step is the Beneficiary Information:

Archive New Transaction Beneficiary

First Name

The first name of the person's receiving the virtual asset. This name should match the one found in the person's passport or legal identity document.

Last Name

The last name of the person's receiving the virtual asset. This name should match the one found in the person's passport or legal identity document.

Wallet Address

The wallet address of the person's receiving the virtual asset. An example of this could be bc1phdhfqtjwwweu3xdsw40mje5g48cgnnks6u00ajaf7xh3jn8vtk3srps2sp for bitcoin or 0xcBfa884044546d5569E2abFf3fB429301b61562A for ethereum.

2. Virtual Asset

Archive New Transaction Virtual Asset

Virtual Asset

Here you can find a dropdown with all the crypto assets we support. You should choose the asset used in the transaction. If we do not yet support a crypto asset you need, you can always contact the 21 Analytics team and request it.

Amount

The amount of the virtual asset selected for the transaction.

Transation ID (TxID)

A virtual asset transaction will create a Transaction ID for this particular transaction in the blockchain. You can usually find these either on your wallet or on a blockchain explorer.

3. Originator Information

Archive New Transaction Virtual Asset

First Name

The first name of the person sending the virtual asset. This name should match the one found in the person's passport or national identity document.

Last Name

The last name of the person sending the virtual asset. This name should match the one found in the person's passport or national identity document.

Internal Account Number

A number used to identify your customer within your system. This can be mapped to an external custodial system. Each customer should have a unique Account Number.

Originator VASP LEI (optional)

The VASP LEI (Legal Entity Identifier) of the VASP sending the virtual asset, this will most likely be your VASP LEI. These are public and can be found in the Global Legal Entity Identifier Foundation (GLEIF) website. This field is optional.

Originator VASP Name

The name of the VASP sending the transaction. A dropdown will appear with all the VASPs added to using the email protocol in Counterparty VASPs. You can also add a different one if that one doesn't appear.

Upload Visual Proof (optional)

Allows you to add additional proof by uploading a screenshot or screen recording of the transaction. This field is optional.





Under address you can find the following fields:

Street Name (optional)

The name of the street as found on the passport or national identity document of the person sending the transaction. This field is optional.

Building Number (optional)

If an address line or street name was added this will be the number of the builing found on the passport or national identity document.

Address Line (optional)

The addess line as found on the passport or national identity document of the person sending the transaciton. This field is optional.

Postal Code

The postal code of where the originator customer resides, as found on their passport or national identity doucument.

City

The city of where the originator customer resides, as found on their passport or national identity doucument.

Country

The country of where the originator customer resides, as found on their passport or national identity doucument.

Archived Transactions Overview

Our archived transactions overview lets you easily navigate through all transactions that have gone through one of our supported protocols.

Transactions Inbox Table

Archived Transactions Overview Table


Let's now do a breakdown of the tables row.

Transactions Inbox Table

Archived Transactions Overview Table Row


As you can see, this row contains the following fields:

Type
Status
Beneficiary
Originator
Originator VASP
Virtual Asset
Amount
Date Finalised

Type

Can be either Sent or Received. Hovering over the icon will reveal a tooltip with the type of transaction.

Transaction Type Received

When the Type has the Received value, represented by the box logo above, a counterparty VASP has sent you, the compliance officer, a transaction. This transaction was either Executed, Declined, Canceled or Registered.

Transaction Type Sent

When the Type has the Sent value, it means you have sent a transaction to a counterparty VASP through the Send Transactions Notifications button. This transaction was either Executed, Declined or Canceled.

For a better understanding of the whole workflow, check: Advanced Workflow.

Status

On the archived tab there are a few possible statuses:

The best and most common status should be Executed.

Transaction Status Executed

If most of your transactions have the status of Executed this means things are working correctly and the transaction is compliant; one of the viable protocols has been used to verify this transaction as compliant.

If the transaction has the status of Declined this means the beneficiary VASP has decided to not allow the transaction to go through. This could be due to KYC checks not meeting the requirements expected by the beneficiary VASP.

Transaction Status Declined

If the transaction has the status of Canceled this means the beneficiary VASP approved it, but then the originator decided to cancel the transaction from going through at the last stage of the handshake. This could be due to KYC checks ran on the beneficiary VASP not being validated.

Transaction Status Canceled

Last of all, as a fallback to the viable protocols, transactions can be manually archived. This might not be fully compliant in some cases but it's useful to keep for future references or for countries that request less information. This will go into a status of Registered.

Transaction Status Registered

Beneficiary

The name and surname, as registered in the passport or national identity document of the person receiving the transaction.

Originator

The name and surname, as registered in the passport or national identity document of the person sending the transaction.

Originator VASP

The VASP of the customer who sent the transaction.

Virtual Asset

Transactions Virtual Asset

Shows the symbol, name and currency code of the Virtual Asset Sent or Received in the transaction. Clicking on the name will redirect you to coinmarketcap, where you can see live information about the transaction’s asset.

Amount

The amount of a particular Virtual Asset sent from the Originator to the Beneficiary. Yes, we do support the entire 18 decimals for Ethereum.

Date Finalised

The time and date the transaction was either Executed, Declined, Canceled or Registered. The format shown is day/month/year and hour/minutes/seconds. The time will be shown in the time zone configured in your browser and can be seen by hovering over the transaction date. This will reveal a tooltip with the timezone: continent/city format.

Detailed Transaction

When you click on one of the rows in the Transactions Overview table, a popup modal will display.

Archived Transactions Details

Travel Transaction ID

At the top left of the modal, you can find a title. This title shows the ID of the transaction in our software. This should not be confused with the blockchain transaction ID. This is used to find specific transactions from all your list of transactions.

Transaction ID

This field is only visable if the transaction is of type Received and was Executed or Registered. This is the transaction ID generated on blockchain for this particular transaction. This can be found on the wallets of the originator or beneficiary as well as in a blockain explorer.

Transaction Status

Can be either Executed, Declined, Canceled or Registered.

Transaction Status Executed

If the transaction is of status Executed it means the transaction went through correctly and the transaction is compliant.

Transaction Status Declined

If the transaction has the status of Declined this means the beneficiary VASP has decided to not allow the transaction to go trough. This could be due to KYC checks not meeting the requirements expected by the beneficiary VASP.

Transaction Status Canceled

If the transaction has the status of Canceled this means the beneficiary VASP approved but then the originator decided to cancel the transaction from going through at the last stage of the handshake. This could be due to KYC checks ran on the beneficiary VASP not being validated.

Transaction Status Registered

Last of all, as a fallback to the viable protocols, transactions can be manually archived, this might not be fully compliant in some cases but it's useful to keep for future references or for countries that request less information. This will go into a status of Registered.

Transaction Type

Can be either Received or Sent.

Transaction Type Received

When the Type has the Received value, it means a counterparty VASP has sent you a transaction or you have manually archived a transaction.

Transaction Type Sent

When the Type has the Sent value, it means you have sent a transaction to a counterparty VASP through the Send Transactions Notifications button, and then the transaction was either approved, declined or canceled.

For a better understanding of the whole workflow, check: TRP.

Protocol

The protocol used to check if the transaction was compliant.

Date Created

The time and date the transaction was Sent or Received. The format shown is day/month/year and hour/minutes/seconds. The time will be shown in the time zone configured in your browser and can be seen by hovering over the transaction date. This will reveal a tooltip with the timezone: continent/city format.

Date Finalised

The time and date the transaction was either Executed, Declined, Canceled or Registered. The format shown is day/month/year and hour/minutes/seconds. The time will be shown in the time zone configured in your browser and can be seen by hovering over the transaction date. This will reveal a tooltip with the timezone: continent/city format.

Virtual Asset

Transaction Virtual Asset

Shows the symbol, name and currency code of the Virtual Asset Sent or Received in the transaction. Clicking on the name will redirect you to coinmarketcap, where you can see live information about the transaction’s asset.

Amount

Transactions Amount

The amount of a particular Virtual Asset sent from the Originator to the Beneficiary. Yes, we do support the entire 18 decimals for Ethereum. You can also find the equivalent of the chosen Virtual Asset as a US dollar equivalent. This is fetched from messari.com, and if you hover over the USD value, you will see the date and time when the inquiry was converted.

Comment

If you have received a transaction the GUI allows you to add a comment on the approve decline step. This comment is internal and won't be seen by the originator VASP.

Risk Score

This field will appear if you setup a risk analysis tool such as Chainalysis, and the transaction is of type Received and was Executed using TRP. For this you need to add the Chainalysis API key in the configure your VASP page.

Beneficiary Name

The first and last name of the customer who is receiving the virtual asset.

Beneficiary VASP Name

The name of the VASP of the customer receiving the virtual asset. This name is associated with a LEI.

Beneficiary VASP LEI

The LEI (Legal Entity Identifier) of the VASP who is receiving the virtual asset.

Beneficiary Wallet Address

The Wallet Address of the customer receiving the virtual asset of the transaction. This field will only show up if the transaction is of type Received and was Executed or Registered.

Originator Name

The first and last name of the customer sending the virtual asset.

Originator VASP Name

The name of the VASP of the customer sending the virtual asset. This name is associated with a LEI.

Originator VASP LEI

The LEI (Legal Entity Identifier) of the VASP who is sending the virtual asset.

Originator Internal Account Number

An account number to identify the customer sending the transaction. Each customer should have their own Account Number.

Originator Address

The Address where the customer resides. This can be found on their passport or country ID.

Export

Export Button

This will download an .xlsx spreadsheet with the data of this particular transaction. This can later be imported to any spreadsheet software for further manipulation of data.

Download Report

Download Report Button

This will download a .doc file with a written report template of the transaction. You can manipulate this on text editing software. Take into account this button will not show on transactions that used email as a protocol.

View Visual Proof

Download Report Button

On manually archived transactions you can view a visual proof of the transaction. By clicking on this button the file that was uploaded will appear for future referance.

Export Transactions

Export Transactions Button

The Export Transaction button allows you to export all the archived transactions as an .xlsx file. This can then be imported to a spreadsheet software to manipulate further if needed, which is handy for filtering and ordering data in a specific way. It also allows you to search for a specific entry. The 21 Analytics team prioritised the export functionality since it allows compliance officers to use software they are familiar with and allows for an additional storage method. Please contact our team if you have any extra functionality requirements.

Pagination

Once enough transactions have been populated, pagination appears at the bottom left, under the table.

Transactions Pagination

A new page will be created when 15 rows have been populated on the overview table. You can navigate these by either clicking on the page number or going to the next or previous pages using the arrows.

Note: Our screenshots currently display 13 rows. This was done to show extra elements like the Export Transactions button. The software will display 15, as mentioned.

Self-hosted Wallets

This is the page used to verify self-hosted wallets to prove address ownership.

This page consists of:

Get AOPP Link Button

Address Ownership Proof Methods:

Proof Methods Buttons

Proofs Overview

Proofs Overview

Export Proofs

Export Proofs

Get AOPP Link

The Get AOPP Link button allows you to create a .pdf your customer can use to verify address ownership. To verify address ownership, you can press the following button:

Get AOPP Link Button

This will prompt a modal to verify bitcoin (BTC) or ethereum (ETH) transactions.

Get AOPP Link Bitcoin Deposit

If you choose bitcoin, you will see the option to Deposit or Withdraw. You need to choose the desired account that will be used for the digital signature. You then need to add the name of the customer who wishes to verify the address ownership. For ethereum, there is only one option since addresses work for both.

Once you click on the Get AOPP Link, a new tab will open.

Get AOPP Link PDF Page

This will have a QR code and a link the customer can use with their wallet to verify ownership. You can now go to file -> print on your browser header and save it as a .pdf to send to your customer. The customer can now verify they own the self-hosted wallet address by scanning the QR code with their wallet, provided the wallet supports AOPP.

Signature Proof

Note: We recommend that you automate this manual proccess using the API.

Signature Proof allows you to verify address ownership through a digital signature. This digital signature should be created on a self-hosted wallet. You can add the proof by clicking on the following button:

Signature Proof Button

A modal will prompt and require you to add some information to verify ownership.

Signature Proof Button

This data should be copied and pasted from the customer's self-hosted wallet and sent to you. If you add the data and the signature is valid, it means the customer could create the digital signature from their self-hosted wallet.

Satoshi Test

Note: We recommend that you automate this manual proccess using the API.

This proof method involves the customer sending a minimal amount of a virtual asset to verify they own the self-hosted wallet account. To initiate a Satoshi Test, you can click the following button:

Satoshi Test Button

A modal will appear requesting the VASP Deposit Address, the Customer's Wallet Address and the Virtual Asset.

Satoshi Test Button

The first would be the address the customer holds at your VASP, and the second will be the address you want to verify ownership of. Lastly, the currency used.

The modal also shows you the deposit time window. This defaults to 48 hours.

Once all the data has been added and you clicked on the Add Satoshi Test button, you will see the following message:

Satoshi Test Message

This message can be sent to the customer so that they can do as the message instructs. If you wish to change the amount that should be transferred, you can always modify this in the Configure Your VASP settings.

Once the transaction has been registered on the blockchain, you can verify the ownership of the address in the Proofs Overview entry.

Satoshi Test Row

Visual Proof

Note: We recommend that you automate this manual proccess using the API.

This verification ownership method allows you to upload an image or video of a screenshot to prove the ownership of a customer's self-hosted wallet. You can add a new visual proof by clicking the following button:

Visual Proof Button

This will prompt a modal where you can upload a screenshot and add the address of the self-hosted wallet of your customer.

Visual Proof Modal

Proofs Overview

Our proofs overview allows you to easily navigate through all the self-hosted address ownership proofs.

Proofs Overview

The table consists of the following fields:

Wallet Address
Status
Virtual Asset
Proof Type
Proof
Date Created
Actions

Wallet Address

The wallet address that is being checked for proof of ownership. These refer to an external self-hosted address of one of the customers of your VASP.

Status

Can be either Verified, Pending or Failed.

Verified

Proofs that were successfully confirmed will go into a Verified status.

Pending and Failed are specific to the Satoshi Test proof type.

Pending

If it’s Pending, it means the blockchain transaction has not yet been transferred; this means the wallet ownership is not yet confirmed.

Failed

If the specified amount was not sent within the 48 hour timeframe from the Date Created, the transaction will fail, and there will be no proof of ownership for this particular address.

Virtual Asset

The Virtual Asset that the external self-hosted wallet holds.

Proof Type

The method type used to show proof of ownership of the customer's self-hosted wallet. We currently have three options:

  • Signature Proof
  • Satoshi Test
  • Visual Proof

Proof

These can vary depending on the Proof Type used.

Signature Proof

If you used the signature proof method, this field would show the message used to create the digital signature.

Satoshi Test

If successfully verified, this will show a message like the following:

Received 0.00005175 BTC in 232025407cae6b158c67af3d4c590012f...

if pending, it will show the following message:

14UAZPRVtNb5T5nbRLoGJmj5U6n9CmeY2Q must be verified within 48h

If you hover over this, a tooltip will display the full message with a copy button for the whole message.

Satoshi Test Pending Messager

In the case the transaction fails, the field will show something like this:

Expired without proof on 21/02/2023, 13:46:18

Visual Proof

For Visual Proof, this field will show a button to view the proof. This should open a new tab with the file.

Satoshi Test Pending Messager

Date Created

The time and date the proof method was created. The format shown is day/month/year and hour/minutes/seconds. The time will be shown in the time zone configured in your browser and can be seen by hovering over the transaction date. This will reveal a tooltip with the timezone: continent/city format.

Actions

There are two actions available on the actions field; the Save As File and the Verify buttons.

Save As File

Save As File Button

This allows you to download an .xlsx with the data about this proof.

Verify

Save As File Button

This action is specific to the Satoshi Test. Once the transaction has been checked on either a VASP wallet address or a blockchain explorer, you can click this button to verify the transaction.

Export Proofs

Export Proofs

The Export Proofs button allows you to export all the proofs as an .xlsx file. This can then be imported to a spreadsheet software to further manipulate if needed, which comes in handy for filtering and ordering data in a specific way. It also allows you to search for a specific entry.
The 21 Analytics team prioritised the export functionality since it allows compliance officers to use software they are familiar with and allows for an additional method of storage. Please contact our team if you have any extra functionality requirements.

TRP

Authoritative source

Travel Rule Protocol

VersionDateDescription
1.0.019th August 2020Initial Public Version
1.0.110th December 2020Bug fix
1.1.08th February 2021Clarifications, bug fixes and consistency enhancements
2.0.010th May 2021Addition of Travel Address enhanced flow
2.1.031st Oct 2021Travel Address advanced flow with inquiry tag
3.0.015th Feb 2022mTLS for client and server authentication
deprecate enhanced workflow
3.1.026th Jun 2023reworked travel address specification
3.2.010th Jul 2023Add DTI as new token identifier

Executive Summary

Inherently, involvement within the virtual asset ecosystem presents higher risks related to money laundering and terrorist financing than those found in the traditional world of fiat currency. Primarily this is due to the lack of transparency of the public keys, which are not associated with any identity.

Whilst in the traditional world, there is proven infrastructure with processes and tools designed to mitigate AML/CFT risks, given the evolution of the virtual asset ecosystem, there is now a need to find comparable solutions not only to address industry concerns but incoming global regulations. Key operators such as custodians and market participants must consider the following issues or gaps:

  1. The current inability to identify originators and/or beneficiaries of transactions.
  2. Cumbersome processes and governance to be put in place in order to confirm to whom a defined public key belongs.

There are varying approaches across the industry to define a common set of standards and to create a mechanism for the exchange of relevant information including identity and associated address details all using a common infrastructure, protocol and data set.

Introduction

The Travel Rule working group was primarily established to address a specific recommendation issued by the Financial Action Task Force ("FATF"). Members of the working group include leading industry participants from around the world who meet regularly to discuss and agree on finding a common solution to meet the regulatory standards. The information within this paper seeks to highlight a solution, referred herein as the Travel Rule Protocol (TRP).

The TRP vision is to define a set of standards which will allow Virtual Asset Service Providers (VASPs - as defined as in footnote1 below) to safely share with other trusted participants the identity of an originator and beneficiary linked to a specific virtual asset transfer. This set of standards aims to meet the requirements set by the FATF Recommendation 16 (R16), commonly known as the Travel Rule defined as below:

R16 -- Countries should ensure that originating VASPs obtain and hold required and accurate originator information and required beneficiary information on virtual asset transfers, submit the above information to the beneficiary VASP or financial institution (if any) immediately and securely, and make it available on request to appropriate authorities. Countries should ensure that beneficiary VASPs obtain and hold required originator information and required and accurate beneficiary information on virtual asset transfers, and make it available on request to appropriate authorities. Other requirements of R16 (including monitoring of the availability of information, and taking freezing action and prohibiting transactions with designated persons and entities) apply on the same basis as set out in R16. The same obligations apply to financial institutions when sending or receiving virtual asset transfers on behalf of a customer.

It is intended that adoption of the TRP is royalty and license free. However, any fees and licenses related to implementation, service of software providers, etc. will be the responsibility of the implementing VASP, including but not limited to document the legal and contractual arrangement ensuring that any exchange of information is done in compliance with applicable laws, e.g. GDPR.

The TRP has been incrementally developed by a self-selecting group of leading industry participants from around the world, collaborating openly and sharing to develop and evolve the solution. There is no single central body or organization behind the protocol, only collaboration.

The TRP working group has a regular weekly minuted meeting with a set agenda. There are other forums including chat rooms for more frequent collaboration as well as email for more infrequent formal announcements. The working group welcomes all contributions, ideas and input to the design and implementation of the protocol. Industry participants who wish to help developing the protocol, or adopt the protocol are encouraged to register at our form.

Any comments, corrections or suggestions on this version of the protocol specification can also be captured via the GitLab.

Goals

The goal of the TRP is to develop a first-generation, minimal, workable, and pragmatic API specification for compliance with the FATF Travel Rule in a timely manner, that also minimizes fragmentation and maximizes interoperability.

First Generation

Industry participants acknowledge that mainstream adoption of virtual assets as an asset class will take time, and encouragingly there is evidence that its evolution is progressing with increasing focus by regulators and central banks imposing regulatory requirements. The TRP aims to initiate one of the first steps on this journey by launching a first-generation solution for compliance with the FATF Travel Rule.

Minimal, Workable, Pragmatic API

The TRP has been developed with sufficient flexibility allowing straightforward adoption for a wide range of industry participants. By focusing closely on FATF R16 and limiting the scope of the solution, the TRP aims to create a simple API specification for required information exchange that will seamlessly integrate with existing business IT frameworks.

Minimize Fragmentation & Maximize Interoperation

There are a number of potentially more comprehensive Travel Rule solutions in development across the industry participants. By focusing closely on FATF R16 and limiting the scope of the solution, the TRP aims to create a simple API specification for the information exchange that is easy to implement and integrate with existing business solutions, as well as having an easy path to interoperate with other emerging industry Travel Rule solutions as they mature. Any investment made should be transferable to future industry solutions.

Design Philosophy

The underlying design principles of the Travel Rule Protocol:

  1. Compliance with FATF R16
  2. Data privacy: Bilateral exchange for minimal data leakage
  3. Iterative and incremental with early MVP launch
  4. Open for industry collaboration and adoption
  5. Aligned with emerging industry standards such as IVMS101
  6. Any investment made should be transferable to future industry solutions
  7. Each member controls the data that they share

Indication of Requirement Levels

This document follows rfc2119 to indicate requirement levels.

Unspecified Behavior

TRP leaves certain aspects of the protocol unspecified. It is a deliberate decision that enables to accommodate as many use cases as possible. This is a necessity due to the nature of travel rule which spans various jurisdictions and needs to be applicable to a diverse variety of VASPs.

We use the following sanity check when judging about unspecified behavior:

When two VASPs implement a certain aspect differently but still are able to interoperate then we MUST omit detailing that aspect in the specification.

If you have found cases that are unspecified but violate the above sanity check then this is considered a bug and we'd kindly ask you to open an issue.

Example

The layout for callback URLs is unspecified because when two VASPs implement them differently they are still able to interoperate. Leaving them unspecified gives VASPs the freedom to encode any data into the URL that the VASP might find useful. It also allows us to extend the protocol without breaking backwards compatibility with respect to callback URLs.

Travel Address

In TRP, the originator VASP receives a special payment address, called Travel Address, instead of a normal blockchain address from the end user. This Travel Address is a base58check encoded, unique URL with a "ta" string prefix. The encoding MUST use the bitcoin alphabet. The URL MUST contain the query pair t=i which stands for "tag = inquiry". It is used to identify the URL type and to allow for future extensibility. The URL MUST omit the scheme part, e.g. https://. It is already determined in the protocol specification and, hence, redundant information.

The following code defines reference travel address encoding/decoding functions for Python >= 3.9. Error handling has been omitted for brevity.

def encode_travel_address(url: str) -> str:
    import based58
    return f"""ta{based58.b58encode_check(url.removeprefix("https://").encode()).decode()}"""

def decode_travel_address(ta: str) -> str:
    import based58
    return based58.b58decode_check(ta.removeprefix("ta").encode()).decode()

Once the Travel Address is decoded by the originator VASP a request is made to that URL to inquire for permission to execute an transaction.

This mechanism hence allows to:

  • decline transfers
  • conduct KYC checks
  • easily share transaction ids

which solves many practical problems for compliance teams.

The Travel Address is inspired by the LNURL standard but lacks its major shortcomings:

  • base58check results in shorter strings than bech32 used in `LNURL.

  • base58check doesn't have a maximum length limit as opposed to bech32 used in LNURL. Even though the LNURL standard specifically opts out of that limit check (because bech32 is used to encode a URL instead of binary data), in practise it is hard to find libraries that don't enforce that limit.

  • LNURL's name suggests misleadingly that the lightning network is a component of LNURL. While this is strictly not the case we found it hard to explain this to parties not familiar with the LNURL standard.

Travel Address Test Vectors

Encoding

InputExpected Result
beneficiary.com/x/12345?t=ita2W2HPKfHxgSgrzY178knqXHg1H3jfeQrwQ9JrKBs9wv
beneficiary.com/x/12345Error due to missing query string "t=i"
https://beneficiary.com/x/12345?t=iError due to URI scheme presence
beneficiary/x/12345?t=iError due to unresolvable TLD

Decoding

InputExpected Result
ta2W2HPKfHxgSgrzY178knqXHg1H3jfeQrwQ9JrKBs9wvbeneficiary.com/x/12345?t=i
2W2HPKfHxgSgrzY178knqXHg1H3jfeQrwQ9JrKBs9wvError due to missing ta prefix
taEJKtAQyrS5x6i59GBS2fcbcUxoR14dYiW9cZuError due to missing query string "t=i"
ta2BCfkBRHmbhyZuKHmEdHpypmo7HG4RJVgaWYR4LkKGLyAtJQkDtJrKError due to URI scheme presence
taEJKtAQyrS5x6i59GKB6iMPx1iDzs8HXGNhbk1Error due to unresolvable TLD

Minimal Core Protocol with Extensions

The first part of the goals of the TRP:

"first generation, minimal, workable, and pragmatic API specification for compliance with the FATF Travel Rule in a timely manner"

is centered around having a simple minimal protocol that is easy to implement and to comply with.

The second part of the goals is to:

"minimize fragmentation and maximize interoperation"

In order to meet both of these goals the TRP is separated into two parts:

  1. A minimal core protocol
  2. Extensions

The minimal core protocol should contain mandatory elements that cover the bare minimum FATF R16 requirements. This meets the first part of the goals.

However, it is acknowledged that one size does not fit all, taking into account differing jurisdictional and compliance requirements on what information is to be exchanged. To help accommodate the second goal an extension mechanism is proposed.

The minimum core protocol will comply with the mandatory elements of R16, all other requirements will be added using the extension mechanism.

Security

TRP uses TLS 1.3 to secure all protocol traffic. This is a well-understood and widely-adopted solution for communication security.

Client Authentication

Clients are authenticated using a X.509 certificate. Those are most commonly used to authenticate a server when using TLS.

It is referred to as mutual TLS (mTLS) when both, client and server, are authenticated using X.509 certificates.

In TRP, a server MUST implement the /identity endpoint. A server's client certificate MAY be obtained by a GET request to the /identity endpoint. It is returned in a JSON document to allow for future extension. The expected response looks as following:

HTTP 200

{
    "x509": [x509 certificate in PEM format]
}

The PEM format is of type string. Further details can be obtained here.

TRP servers are required to reject connections from clients that cannot authenticate themselves using mTLS. The only exception is the /identity endpoint.

A VASP is free to choose any CA provider. In TRP, mTLS is used purely for establishing a secure communication channel. There is no additional semantics attached to the X509 certificates.

Because mTLS is established and well-understood it can be used without custom implementation out-of-the-box with nginx.

Technical implementation - Core Protocol

Headers

Each request must have the following header fields:

FieldDescriptionExample value
api-versionVersion of the API3.2.0
request-identifierUnique UUID V4 value for this particular API call (different for all calls)2351f3f1-bcff-4a06-a07a-8de94220a9b0

Each response must include the headers api-version and request-identifier. The former holds the version of the API the responder is using, the latter echoes the request-identifier back to the sender.

Sample Header

api-version: 3.2.0
request-identifier: 2351f3f1-bcff-4a06-a07a-8de94220a9b0

Motivation

TRP solves the following issues:

  1. enables to decline transactions a priori by using a transfer inquiry. This reduces the workload significantly for the compliance team.

  2. enables to negotiate a payment address after the KYC checks have been completed. This eliminates most unwanted payments.

  3. enables to share the transaction id after the blockchain payment has been executed by using the transfer confirmation. This eliminates the need for signed but unbroadcasted transactions that require low-level tinkering and are risky because they can be executed by simply broadcasting them. The transaction id is useful for the beneficiary to check if he has received the payment and trigger a human interaction in case it wasn't received.

Overview

Note that the sequence diagram below embeds the travel rule protocol into a wider context for illustration purposes. This includes user interaction via user interface, payee to payer interaction and on-chain transactions. The steps belonging strictly to the protocol are enumerated in steps one to three.

Hence, VASPs MAY diverge in steps depicted in the diagram that are not part of the three protocol steps. There are plausible reasons why a VASP would decide to do so:

  • to better accommodate to its risk based approach

  • to adapt to legal requirements of a particular jurisdiction

  • to work around legacy systems with limited integration capabilities

E.g. VASPs MAY decide to also accept a wallet address (depicted in grey) as an user interface input field and execute the virtual asset transaction before receiving an approval from but not before sending IVMS 101 payload to the beneficiary VASP. Please note, that following such an user interaction defeats the advantages listed in the motivation section above. VASPs MUST ensure that any interactions they decide for are compliant with the law in their jurisdiction.

actor "ORIGINATOR USER" #lightblue
entity "ORIGINATOR VASP" #blue

entity "BENEFICIARY VASP" #purple
actor "BENEFICIARY USER" #lightgreen

group "BENEFICIARY USER" wants to receive virtual assets from "ORIGINATOR USER", Happy Path

group User Interaction
"BENEFICIARY USER" -> "BENEFICIARY VASP": I would like to receive funds
"BENEFICIARY VASP" -> "BENEFICIARY USER": Generates Travel Address ta6MNBj2u4art71g... which encodes\nbeneficiary.com/api/3fc3645b439c?t=i
"BENEFICIARY USER" -> "ORIGINATOR USER": Gives Travel Address (ta6MNBj2u4art71g...)\n+beneficiary details <font color=grey>+optional wallet address
"ORIGINATOR USER" -> "ORIGINATOR VASP": Sends P1=(Travel Address,\nBeneficiary Details)
"ORIGINATOR VASP" -> "ORIGINATOR VASP": Sanctions/PEP Checks Beneficiary Details
end

autonumber
"ORIGINATOR VASP" -> "BENEFICIARY VASP": **POST to beneficiary.com/api/3fc3645b439c?t=i (decoded Travel Address)**
note right: {\n  "IVMS101": ...,\n  "asset": {\n    "dti": [digital token identifier as per Digital Token Identifier Foundation]\n  },\n  "amount": [amount in lowest denomination],\n  "callback": "https://originator.com/inquiryResolution?q=4585839457"\n}

autonumber stop
"ORIGINATOR VASP" -[#grey]-> "BENEFICIARY VASP": <font color=grey>Optionally executes a virtual asset transfer resulting in a tx identifier
"BENEFICIARY VASP" -> "BENEFICIARY VASP": Sanctions/PEP Checks on Originator User Details
autonumber resume

"BENEFICIARY VASP" -> "ORIGINATOR VASP": **POST to https://originator.com/inquiryResolution?q=4585839457**
note left: {\n  "approved": [payment address],\n  "callback": "https://beneficiary.com/transferConfirmation?q=3454366424"\n}

autonumber stop
"ORIGINATOR VASP" -> "ORIGINATOR VASP": On chain analytics on [payment address]
"ORIGINATOR VASP" -> "BENEFICIARY VASP": Executes a virtual asset transfer resulting in a tx identifier
autonumber resume

"ORIGINATOR VASP" -> "BENEFICIARY VASP": **POST to https://beneficiary.com/transferConfirmation?q=3454366424**
note right: {"txid": [some asset-specific tx identifier]}
end

Detailed Protocol Flow

An example protocol flow looks like this:

ta6MNBj2u4art71gN99C6xniYrNfDUF26QgZHdXwd6zzYsvk1S6M5A15K2REHRULnZHnJs88

It encodes:

beneficiary.com/implementation/defined/path?t=i
{
    "asset": {
        "dti": [digital token identifier as per Digital Token Identifier Foundation],
    },
    "amount": [amount in lowest denomination of asset],
    "callback": "https://originator.com/implementation/defined/path/for/inquiryResolution",
    "IVMS101": {
        "originator": {
            "originatorPersons": [
                {
                    "naturalPerson": {
                        "name": {
                            "nameIdentifier": [
                                {
                                    "primaryIdentifier": "Post",
                                    "secondaryIdentifier": "Johnny",
                                    "nameIdentifierType": "LEGL"
                                }
                            ]
                        },
                        "geographicAddress": [
                            {
                                "addressType": "GEOG",
                                "streetName": "Potential Street",
                                "buildingNumber": "123",
                                "buildingName": "Cheese Hut",
                                "postCode": "91361",
                                "townName": "Thousand Oaks",
                                "countrySubDivision": "California",
                                "country": "US"
                            }
                        ],
                        "customerIdentification": "1002390"
                    }
                }
            ]
        },
        "beneficiary": {
            "beneficiaryPersons": [
                {
                    "naturalPerson": {
                        "name": {
                            "nameIdentifier": [
                                {
                                    "primaryIdentifier": "MachuPichu",
                                    "secondaryIdentifier": "Freddie",
                                    "nameIdentifierType": "LEGL"
                                }
                            ]
                        }
                    }
                }
            ]
        },
        "originatingVASP": {
            "originatingVASP": {
                "legalPerson": {
                    "name": {
                        "nameIdentifier": [
                            {
                                "legalPersonName": "VASP A",
                                "legalPersonNameIdentifierType": "LEGL"
                            }
                        ]
                    },
                    "nationalIdentification": {
                        "nationalIdentifier": "506700T7Z685VUOZL877",
                        "nationalIdentifierType": "LEIX"
                    }
                }
            }
        }
    }
}

DTI is a digital token identifier as per Digital Token Identifier Foundation. It enables the unique identification of digital tokens based on ISO 24165. For more information, see the DTI Implementation Guide.

The DTI of Bitcoin is 4H95J0R2X.

Response data

HTTP OK 200

{
    "version": "3.2.0"
}

or, in case the inquiry is auto-approved,

{
  "approved": {
    "address": "some payment address",
    "callback": "https://beneficiary.com/implementation/defined/path/for/transferConfirmation"
  }
}

or, in case the inquiry is auto-rejected,

{
    "rejected": "human readable comment" [or null]
}

Transfer Inquiry Resolution

The callback URL from the transfer inquiry is used to send the following POST request

{
    "approved": {
        "address": "some payment address",
        "callback": "https://beneficiary.com/implementation/defined/path/for/transferConfirmation"
    }
}

or

{
    "rejected": "human readable comment" [or null]
}

Response data

HTTP 204 No Content

Transfer Confirmation

The callback URL from the transfer inquiry resolution is used to send the following POST request

{
    "txid": [some asset-specific tx identifier]
}

or

{
    "canceled": "human readable comment" [or null]
}

The txid should be communicated only if - as the name of the API endpoint suggests - the transaction has been confirmed.

Response data

HTTP 204 No Content

General error handling

Invalid/Expired URLs

A Travel Address can expire (e.g. customer closes account) or be invalid (e.g. someone crafts a URL with arbitrary parameters). Also a callback URL can expire (e.g. already called or timed out) or be invalid by construction. A backend must reply in such a case with an HTTP status code 404 NOT FOUND.

Error on server side

If an error is triggered on the server side (e.g. database unreachable) then a backend must reply with HTTP status code 500 INTERNAL SERVER ERROR.

Extensions

Entirely optional, zero or more extensions can be supported. Users of the TRP protocol should agree on a bilateral basis on which extensions they will use.

Scope

Extensions can add any and all HTTP headers or parameters.

The body of the requests can be added to the designated location.

Caveats

Conflicts between extensions

With the number of extensions growing the risk of conflicts or incompatibilities between extensions grows exponentially. New extension authors should take special care to investigate and address any conflicts.

Promotions from extension to core

Extensions might prove a comfortable testing ground for new functionality. When an extension is universally used it could be promoted to the core protocol. The promotion mechanism is still to be decided.

Fallback

Implementations should cater to as many extensions as possible. Some form of auto-discovery and if practical fallback should be catered for.

Core protocol additions

In order to support extensions some extension-specific handling is required in the core protocol beyond what is necessary for R16 compliance. These are described in the following section.

API-Extensions header

The support of an extension is signaled through the HTTP header field api-extensions.

FieldDescriptionExample value
api-extensionsComma separated list of extensions to the base the TRP protocol to be used when processing this requestrequest-signing,beneficiary-details-response

api-extensions must be omitted when no extensions are enabled.

Sample header with API-extensions:

api-version: 3.2.0
api-extensions: request-signing,beneficiary-details-response
request-identifier: 2351f3f1-bcff-4a06-a07a-8de94220a9b0
Extension not supported response code

A server should be able to inform a client that it is unable or unwilling to support the extensions as defined in the client request's api-extensions header. Two HTTP response codes are applicable, the 422 - Unprocessable Entity and the 501 - Not Implemented. Either one of them is applicable in different extension cases.

Request body

Extensions have a reserved top level key in JSON payloads called extensions. Each extension then has a key below that with a specific payload. For example:

{
  "IVMS101": ...
  "extensions": {
    "offchainData": {
      "refId": ...,
    }
  }
}

When no extensions are used in the request, i.e. the api-extensions header is absent, the key extensions must be omitted.

Compliance with FATF Recommendation 16

The key part of the FATF VASP Recommendations is:

A. As described in INR.15, paragraph 7(b), all of the requirements set forth in Recommendation 16 apply to VASPs or other obliged entities that engage in VA transfers, including the obligations to obtain, hold, and transmit required originator and beneficiary information in order to identify and report suspicious transactions, monitor the availability of information, take freezing actions, and prohibit transactions with designated persons and entities.

By exchanging originator and beneficiary information between VASPs, the TRP facilitates compliance with this requirement. Information must be stored by each VASPs themselves. No central or distributed database is proposed.

A. Further, countries should ensure that beneficiary institutions (whether a VASP or other obliged entity) obtain and hold required (not necessarily accurate) originator information and required and accurate beneficiary information, as set forth in INR. 16. The required information includes the:

  1. originator's name (i.e., the sending customer); The TRP facilitates compliance with this requirement.
  2. originator's account number where such an account is used to process the transaction (eg: the VA wallet); The TRP facilitates compliance with this requirement. The account is the public address.
  3. originator's physical (geographical) address, or national identity number, or customer identification number (i.e., not a transaction number) that uniquely identifies the originator to the ordering institution, or date and place of birth; The TRP facilitates compliance with this requirement. The TRP working group has agreed to use LEI or GEOG from IVMS101 in first phase.
  4. beneficiary's name; and The TRP facilitates compliance with this requirement.
  5. beneficiary account number where such an account is used to process the transaction (eg: the VA wallet). It is not necessary for the information to be attached directly to the VA transfer itself. The information can be submitted either directly or indirectly, as set forth in INR. 15. The TRP facilitates compliance with this requirement. The account is the public address.

Authoritative Version

This document provides a description of the TRP and its usage. Where possible this document will be updated and new versions issued as the protocol is revised.

References

1

See definition of Virtual Asset Service Providers at https://www.fatf-gafi.org/glossary/u-z/

End of Document

AOPP

Authoritative source

Address Ownership Proof Protocol (AOPP)

Mission Statement

The AOPP streamlines and automates address ownership proofs, which are required in interactions between self-hosted wallets and virtual asset service providers (VASPs), for example in virtual asset withdrawal.

Specification

Link on the "Address Ownership Proof" web page of a VASP:

<a href="​aopp:?v=0&msg=vasp-chosen-msg&asset=btc&format=p2xxx&callback=https://vasp.com/proofs/vasp-chosen-token​">verify address</a>

Required parameters in version 0:

  1. v: version number, always 0

  2. msg: a vasp-chosen message up to 1024 ASCII characters; it is recommended to contain a ​nonce​ value

  3. asset: a virtual asset identifier (as defined in ​SLIP-0044​)

  4. format: specifies a wallet address type the VASP expects in a callback;

    1. for "btc" asset it is:

      • p2pkh
      • p2wpkh
      • p2sh
      • p2tr
      • any
    2. for "eth" asset it is:

      • standard
  5. callback: a VASP chosen URL endpoint where the client wallet sends the response to.

When a wallet encounters an unknown version number or other unrecognised or invalid values in required parameters, it should display an error and abort signing. Wallets should ignore parameters not listed above.

On click, a desktop app shows up. The user confirms the message. The app sends a response to the server:

Response:

POST <callback URL>
Content-Type: application/json; utf-8
{
    "version": 0,
    "address": "bc1000000000000000000000000",
    "signature": "​<Bitcoin Signed Message Signature>​"
}

Response from the Server:

  1. HTTP 204: Signature is valid.
  2. HTTP 400: Bad Request.
  3. HTTP 404: The vasp-chosen-token in the callback URL doesn't exist.

Compressed Keys Only

Uncompressed keys are considered legacy and are not supported.

Bitcoin Message Signatures

For bitcoin, the signatures are constructed following the algorithm in Citcoin Core. Special attention should be brought to the encoding of the recovery byte that does not indicate information about witness addresses in its encoding. This is the main difference between bitcoin core's message signatures and other, less popular ones.

Please refer to the wallet guide for further implementation hints.

Ethereum Message Signatures

For ethereum, the signatures are constructed following the same algorithm as in bitcoin core with the notable exception that the recovery byte is appended to the signature instead of prepended.

Recommendations

  1. Choose any for the address format if your VASP supports all possible address formats. Certain wallets (e.g. Electrum) have a fixed address format and therefore letting them pick the address format will increase interoperability.
  2. Choose a small message to improve UX using hardware wallets (small screens). Also, URIs have an implementation-defined limit so smaller URIs will have better interoperability in general.
  3. [Add SRO approved message style here]

FAQ

  1. Why is there a format specifier?

    A format specifier allows a VASP to hint to a wallet what address format it can accept. Historically, certain VASPs have had poor support for various address formats.

  2. How to deal with p2sh addresses?

    For p2sh addresses we need to presume that it's a p2wpkh address wrapped in a p2sh.

  3. Why is the wallet choosing the address?

    Certain wallet types, such as hardware wallets, are limited in how they can handle specific workflows. To facilitate the adoption in all kinds of wallets, the protocol lets the wallet choose the address.

  4. Why is there a limit on the msg parameter?

    Some wallets have a hard limit on the size of the message, hence the 1024 character limit in AOPP.

Test vectors

Bitcoin

  • p2pkh
    • private key: L4MSJRS7EZNoinjUXJAKrtSgvA6epQDAmwgo5B2LJdVCcjEapPE1
    • address: 13LiZwTMfowMo5KsWHf5TNLmK78WSxVQCG
    • message: hello
    • signature: IFwh9XVPb8vvNUsbuuQU1Xk1jT652JD/6HN3cqnFn/MMBDziPbM8cOi83D29LRGSwMF3ZcjytD7nfNdn5dI0/50=
  • p2sh (wrapped SegWit)
    • private key: L5fETAwYWGRA1eWCHk8AgH2FX2rxLp64winwoAoRGpz1aQMp3Rai
    • address: 3JvVkfeKrrJstF66haNpFepfhxzQuBB78h
    • message: hello
    • signature: H1oYVmDaWxZBPEk2ou4myn1SRC20ycBUPPD5fLS+SmQ1e04Bi1J9mIJ5fNhe3khDhJRUX2fU+VHGKlJdAjYIvBU=
  • p2wpkh
    • private key: L5fETAwYWGRA1eWCHk8AgH2FX2rxLp64winwoAoRGpz1aQMp3Rai
    • address: bc1qnshsvhrfl28g03k0vxdez6vua56r0c72xy9e93
    • message: hello
    • signature: H1oYVmDaWxZBPEk2ou4myn1SRC20ycBUPPD5fLS+SmQ1e04Bi1J9mIJ5fNhe3khDhJRUX2fU+VHGKlJdAjYIvBU=

Ethereum

  • private key: 4142e80a872531fd1055f52ccab713d4c7f1eee28c33415558e74faeb516de2b
  • address: 0x270402aeB8f4dAc8203915fC26F0768feA61b532
  • message: hello
  • signature: vbyudz7PM/tUdVjlL0wEtCnvn3PVYv8eCqCf/aLeVj8JwAsUVuyMMwbIInXAj7EtmZIUwlem7AOH0da8ygXmQBs=