COMPLIANCE PROTOCOL
Complying with Anti-Money Laundering (AML) laws requires financial institutions (FIs) to know not
only who their customers are sending money to but who their customers are receiving money from. In
some jurisdictions banks are able to trust the AML procedures of other licensed banks. In other
jurisdictions each bank must do its own sanction checking of both the sender and the receiver. The
Compliance Protocol handles all these scenarios.
The Compliance Protocol is an optional, additional protocol to adhere to
after federation that aids organizations in sharing AML information.
This section specifies standard customer information fields that are exchanged between FIs
via the Compliance Protocol. The most common fields are:
• First Name
• Last Name
• Date of Birth
• Physical Address
GENERAL WORKFLOW
The standard process for financial institutions to interact with each other via the Compliance
Protocol is as follows:
- Each organization that wishes to receive funds and participate in the compliance protocol
creates anAUTH_SERVERand adds its location (URL) to the institution’s kuknos.toml. - The sending FI contacts the receiving FI’s
AUTH_SERVERproviding information on the sender,
whether it needs AML information on the recipient, the unsigned transaction, and any additional
attachments. - The receiving FI responds with whether they’re willing to send AML information on the recipient
and whether they’ll accept the payment, along with additional information pertaining to their
response (such as sending along the AML information of the recipient).
Anchors are free create to implement their own web service that implements the compliance protocol.
IMPLEMENTATION
AUTH_SERVER
The AUTH_SERVER provides a single web endpoint where a compliance request can be sent. The URL is
up to anchors and where anchors host this service, but a simple example would be https://[HOSTNAME]/aml. Once established they’ll need to add this URL to their kuknos.toml under the AUTH_SERVER key.
Sending A Request
To send a compliance request to the receiving financial institution, submit an HTTP POST request to the receiving institution’s AUTH_SERVER with the following specification:
Content-Typeshould equalapplication/x-www-form-urlencoded- The body of the request should be
data=<data value>&sig=<sig value>
Request Components
data is a block of JSON that contains the following fields:
| Name | Data Type | Description |
|---|---|---|
sender |
string | The payment address of the customer that is initiating the send. Ex. bob*bank.com |
need_info |
boolean | If the caller needs the recipient’s AML info in order to send the payment. |
tx |
string: base64 encoded xdr.Transaction | The transaction that the sender would like to send in XDR format. This transaction is unsigned and its sequence number should be equal ۰. |
attachment |
string | The full text of the attachment. The hash of this attachment is included as a memo in the transaction. The attachment field follows the Kuknos Attachment Convention and should contain at least enough information for the sender to allow the receiving FI to do their sanction check. |
sig is the signature of the data block made by the sending FI. The receiving institution should
check that this signature is valid against the public signature key that is posted in the sending
FI’s kuknos.toml (SIGNING_KEY field).
Example Request
Example request body (please note it contains both parameters data and sig):
data=%7B%22sender%22%3A%22aldi%2Abanksender.com%22%2C%22need_info%22%3Atrue%2C%22tx%22%3A%22AAAAAEhAArfpmUJYq%2FQ9SFAH3YDzNLJEBI9i9TXmJ7s608xbAAAAZAAMon0AAAAJAAAAAAAAAAPUg1%2FwDrMDozn8yfiCA8LLC0wF10q5n5lo0GiFQXpPsAAAAAEAAAAAAAAAAQAAAADdvkoXq6TXDV9IpguvNHyAXaUH4AcCLqhToJpaG6cCyQAAAAAAAAAAAJiWgAAAAAA%3D%22%2C%22attachment%22%3A%22%7B%5C%22nonce%5C%22%3A%5C%221488805458327055805%5C%22%2C%5C%22transaction%5C%22%3A%7B%5C%22sender_info%5C%22%3A%7B%5C%22address%5C%22%3A%5C%22678+Mission+St%5C%22%2C%5C%22city%5C%22%3A%5C%22San+Francisco%5C%22%2C%5C%22country%5C%22%3A%5C%22US%5C%22%2C%5C%22first_name%5C%22%3A%5C%22Aldi%5C%22%2C%5C%22last_name%5C%22%3A%5C%22Dobbs%5C%22%7D%2C%5C%22route%5C%22%3A%5C%221%5C%22%2C%5C%22note%5C%22%3A%5C%22%5C%22%2C%5C%22extra%5C%22%3A%5C%22%5C%22%7D%2C%5C%22operations%5C%22%3Anull%7D%22%7D&sig=KgvyQTZsZQoaMy8jdwCUfLayfgfFMUdZJ%2B0BIvEwiH5aJhBXvhV%2BipRok1asjSCUS%2FUaGeGKDoizS1%2BtFiiyAA%3D%3D
After decoding the data parameter it has a following form:
{
"sender": "aldi*banksender.com",
"need_info": true,
"tx": "AAAAAEhAArfpmUJYq/Q9SFAH3YDzNLJEBI9i9TXmJ7s608xbAAAAZAAMon0AAAAJAAAAAAAAAAPUg1/wDrMDozn8yfiCA8LLC0wF10q5n5lo0GiFQXpPsAAAAAEAAAAAAAAAAQAAAADdvkoXq6TXDV9IpguvNHyAXaUH4AcCLqhToJpaG6cCyQAAAAAAAAAAAJiWgAAAAAA=",
"attachment": "{\"nonce\":\"1488805458327055805\",\"transaction\":{\"sender_info\":{\"address\":\"678 Mission St\",\"city\":\"San Francisco\",\"country\":\"US\",\"first_name\":\"Aldi\",\"last_name\":\"Dobbs\"},\"route\":\"1\",\"note\":\"\",\"extra\":\"\"},\"operations\":null}"
}
A few things to note:
memovalue oftxis a SHA256 hash of the attachment.nonceis unique to each individual transaction. When a transaction on the receiving end is
pending, the samenonceshould be sent to fetch updates about thependingtransaction
Server Response
The receiving institution’s AUTH_SERVER will return a JSON object with the following fields:
| Name | Data Type | Description |
|---|---|---|
info_status |
ok, denied, pending |
If the receiving institution is willing to share AML information or not. |
tx_status |
ok, denied, pending |
If the receiving institution is willing to accept this transaction. |
dest_info |
string | Marshalled JSON of the recipient’s AML information. (only present if info_status is ok) |
error |
string | Error message (only present if info_status or tx_status is error or for internal server errors) |
pending |
integer | Estimated number of seconds till the sender can check back for a change in status. The sender should just resubmit this request after the given number of seconds. (only present if info_status or tx_status is pending) |
HTTP status code must be equal:
۲۰۰ OKif bothinfo_statusandtx_statusareok.۲۰۲ Acceptedif both statuses arependingor one ispendingand secondok.۴۰۰ Bad Requestif data sent by the sender is invalid.۴۰۳ Forbiddenif any ofinfo_statusandtx_statusaredenied.۵۰۰ Internal Server Errorfor server side errors.
Examples:
۲۰۰ OK
{
"info_status": "ok",
"tx_status": "ok",
"dest_info": "{\"name\": \"John Doe\"}"
}
۲۰۲ Accepted
{
"info_status": "ok",
"tx_status": "pending",
"dest_info": "{\"name\": \"John Doe\"}",
"pending": 3600
}
۴۰۰ Bad Request
{
"info_status": "ok",
"tx_status": "error",
"error": "Invalid country code."
}
۴۰۳ Forbidden
{
"info_status": "deny",
"tx_status": "ok"
}
۵۰۰ Internal Server Error
{
"error": "Internal server error"
}
Putting It All Together
In this example, Aldi (aldi*banksender.com) wants to send to Bogart (bogart*bankreceiver.com).
If you haven’t read up on Federation yet, we’d suggest you start there
first.
۱) BankSender fetches BankReceiver’s kuknos.toml file
This is done to get BankReceiver’s AUTH_SERVER, FEDERATION_SERVER, and other important
information for BankSender to interact with BankReceiver.
BankReceiver’s kuknos.toml should be hosted at
https://bankreceiver.com/kuknos.toml.
۲) BankSender gets the routing info for Bogart so it can build the transaction
This is done by asking BankReceiver’s federation server to resolve bogart*bankreceiver.com.
BankSender sends an HTTP GET request to [FEDERATION_SERVER]?type=name&q=bogart*bankreceiver.com
See Federation section for a complete description. The returned fields of
interest here are:
- Kuknos AccountID of Bogart’s FI (BankReceiver).
- Bogart’s routing info at BankReceiver.
Example federation response:
{
"kuknos_address": "bogart*bankreceiver.com",
"account_id": "GDJ2GYMIQRIPTJZXQAVE5IM675ITLBAMQJS7AEFIWM4HZNGHVXOZ3TZK",
"memo_type": "id",
"memo": 1
}
۳) BankSender makes the Auth (Compliance) Request to BankReceiver
This request will ask BankReceiver for Bogart’s AML info and for permission to send a transaction
from Aldi to Bogart.
BankSender -> AUTH_SERVER
Example request body (it contains both data and sig):
data=%7B%22sender%22%3A%22aldi%2Abanksender.com%22%2C%22need_info%22%3Atrue%2C%22tx%22%3A%22AAAAAEhAArfpmUJYq%2FQ9SFAH3YDzNLJEBI9i9TXmJ7s608xbAAAAZAAMon0AAAAJAAAAAAAAAAPUg1%2FwDrMDozn8yfiCA8LLC0wF10q5n5lo0GiFQXpPsAAAAAEAAAAAAAAAAQAAAADdvkoXq6TXDV9IpguvNHyAXaUH4AcCLqhToJpaG6cCyQAAAAAAAAAAAJiWgAAAAAA%3D%22%2C%22attachment%22%3A%22%7B%5C%22nonce%5C%22%3A%5C%221488805458327055805%5C%22%2C%5C%22transaction%5C%22%3A%7B%5C%22sender_info%5C%22%3A%7B%5C%22address%5C%22%3A%5C%22678+Mission+St%5C%22%2C%5C%22city%5C%22%3A%5C%22San+Francisco%5C%22%2C%5C%22country%5C%22%3A%5C%22US%5C%22%2C%5C%22first_name%5C%22%3A%5C%22Aldi%5C%22%2C%5C%22last_name%5C%22%3A%5C%22Dobbs%5C%22%7D%2C%5C%22route%5C%22%3A%5C%221%5C%22%2C%5C%22note%5C%22%3A%5C%22%5C%22%2C%5C%22extra%5C%22%3A%5C%22%5C%22%7D%2C%5C%22operations%5C%22%3Anull%7D%22%7D&sig=KgvyQTZsZQoaMy8jdwCUfLayfgfFMUdZJ%2B0BIvEwiH5aJhBXvhV%2BipRok1asjSCUS%2FUaGeGKDoizS1%2BtFiiyAA%3D%3D
After decoding the data parameter it has a following form:
{
"sender": "aldi*banksender.com",
"need_info": true,
"tx": "AAAAAEhAArfpmUJYq/Q9SFAH3YDzNLJEBI9i9TXmJ7s608xbAAAAZAAMon0AAAAJAAAAAAAAAAPUg1/wDrMDozn8yfiCA8LLC0wF10q5n5lo0GiFQXpPsAAAAAEAAAAAAAAAAQAAAADdvkoXq6TXDV9IpguvNHyAXaUH4AcCLqhToJpaG6cCyQAAAAAAAAAAAJiWgAAAAAA=",
"attachment": "{\"nonce\":\"1488805458327055805\",\"transaction\":{\"sender_info\":{\"address\":\"678 Mission St\",\"city\":\"San Francisco\",\"country\":\"US\",\"first_name\":\"Aldi\",\"last_name\":\"Dobbs\"},\"route\":\"1\",\"note\":\"\",\"extra\":\"\"},\"operations\":null}"
}
Please note that the memo value of tx is the SHA256 hash of the attachment, and the payment
destination is what is returned by the federation server. You can check the transaction above using
the XDR Viewer.
۴) BankReceiver handles the Auth request and sends a response to BankSender
- BankReceiver gets sender domain by spliting the sender address (
aldi*banksender.com) in 2 parts:aldiandbanksender.com - BankReceiver also fetches BandSender’s kuknos.toml file at
https://banksender.com/kuknos.toml. This is done to get BankSender’s
SIGNING_KEY. - BankReceiver verifies the signature (the
sigfield) on the Auth Request was signed with
BankSender’sSIGNING_KEY. - BankReceiver does its sanction check on Aldi (the sender) utilizing its own AML/KYC
infrastructure, defined outside of the Kuknos protocol. This determines the value of
tx_statusin the response. - BankReceiver makes the decision to reveal the AML info of Bogart if any of the following
criteria apply:- Bogart has made their info public.
- Bogart has allowed BankSender for transactions.
- Bogart has allowed Aldi for transactions.
- BankReceiver has allowed BankSender for transactions.
- If none of the above criteria are met, BankReceiver should ask Bogart if he wants to reveal this
info to BankSender and accept this payment. In this case BankReceiver will returninfo_status: "pending"in the Auth request reply to give Bogart time to accept the payment or not. - If BankReceiver determines it can share the AML info with BankSender, it sends this info in
dest_infofield with the reply.
Example Response:
{
"info_status": "ok",
"tx_status": "ok",
"dest_info": "{\"name\": \"Bogart Doe\"}",
}
۵) BankSender handles the reply from the Auth request
If the call to the AUTH_SERVER returned pending, BankSender must resubmit the request again after
the estimated number of seconds in the response.
۶) BankSender does AML checks on the receiver (Bogart)
Once BankSender has been given the dest_info from BankReceiver, BankSender does the sanction
check using Bogart’s AML info. If the AML/KYC check passes, BankSender signs and submits the
transaction to the Kuknos network.
۷) BankReceiver handles the incoming payment.
- BankReceiver checks the transaction hash against a cache it has or redoes the sanction check on the sender
(it’s up to the receiving FI to implement any caching for performance). - BankReceiver credits Bogart’s account with the amount sent or sends the transaction back.
