1. Introduction

The Payment Execution or PE is a component that enables clients to accept credit and debit payments from their e-commerce application using Treasury accounts. The PE exposes an API through which clients can create deposits and payouts. The API comes with a callback service providing a mechanism for the integrator to receive updates to their payments.

All communication with the API is secure and payments are backed by a flexible security model giving you the ability to define who is able to view and authorise funds at a granular level. Payments are accessible from Treasury as standard Receive and Send Orders that follow the same state flow and authorization process as orders created manually by a Treasury operator.

2. Communications

The security of the API takes four angles:

  • authentication and data integrity

  • encryption

  • replay attack prevention

  • trusted connections

2.1. Authentication and data integrity

In order to use the PE API two unique values must be generated and used to build every request. These values are a client key and a secret.

Key

Used to identify yourself and all your requests. The key is a public identifier for your client.

Secret

Used to verify the integrity of the data in your messages and to authenticate you on each request.

Find out more about how to generate the key and secret and how they should be included on the request.

2.2. Encryption

All communication is performed over HTTPS providing end-to-end encryption.

2.3. Replay attack prevention

A replay attack occurs when an attacker intercepts a message and then delays or resends it, tricking the server into performing the same request more than once. This can be very dangerous in financial systems if, for example, a request to transfer funds is intercepted and replayed resulting in money being fraudulently moved many times over.

The nonce is a unique number that must be included on each request made by a client. Each time a request is made the value of the nonce must be greater than the value used on the previous request.

2.4. Trusted connections

In order to be able to create and process payments, all clients that wish to connect to the PE API must have their IP addresses added to a whitelist by Customer Support. The whitelist is a list of IP addresses that are allowed to use the PE. This ensures only approved and trusted client connections can access the API.

3. Backward Compatibility

PE provides an API which guarantees backward compatibility between versions.

Here a detailed list of what backward compatibility means:

3.1. Functionality that is not intended to change

These include:

  • existing request parameters

  • existing response/callback fields

  • the set of HTTP request headers that are required for each type of API request

This means the PE API will not add new required HTTP request headers or parameters or change existing optional fields to be required.

3.2. Functionality that may change

These include:

  • add optional request parameters

  • add new values for existing parameters that have enumerated sets of values

  • add optional HTTP request headers

  • add new fields to API responses/callbacks

  • deprecated functionality may be removed or changed at time specified

  • the order of fields in the API response/callback may change

4. Security

4.1. Key and Secret

The API key and secret must be generated before you begin using the API. Find out how to get the secret.

Once you have the values they must then be used in all of your client requests. The key must be included as a header value in the exact format it was received. The secret on the other hand must be used to generate a signature which should also be added to each request as a header value. Find out more about the signature calculation.

The following table shows how each value must be added to the request header:

Header name Value

key

key

signature

HMAC-SHA512(uri + sha256(payload), base64decoded(Secret))

Note: Keep your key and secret safe and don't share them with anyone.

4.2. Nonce

As described above, the nonce is a unique value used to protect against replay attacks. Each time a request is made the value of the nonce must be greater than the value used on the previous request. The nonce is tied to your API key, so the first time you make a request using a client key, any value can be used. For each subsequent request, the value of the nonce must increase.

The nonce is included as part of the payload in each request which requires it.

5. Getting Started

5.1. Get your secret

To begin your integration with the PE API you must first generate a new key and secret. Below you can find a list of steps to achieve this in a secure way. Currently this is a collaborative process between your System Admin team and our Customer Support team.

You can do this by:

  1. Firstly, generating a pair of private and public keys.

    openssl  genrsa  -out  privatekey.txt  1024
    
    openssl  rsa  -in  privatekey.txt  -pubout  -out  publickey.txt
  2. Then, send your public key (publickey.txt) to Customer Support. The public key will be used to encrypt your secret at the point it is generated.

  3. We will send you back an email with the following content:

    {
       "key":  "<new-client-key>",
       "encryptedSecretBase64Encoded":  "<encoded-encrypted-secret>"
    }
  4. This gives you the value for the key, but there are a few more steps required to obtain the secret.

  5. To extract the secret, you must first decode the base64 encoded and encrypted secret using the following commands.

    echo  "<encoded-encrypted-secret>"  >  key.bin.enc
    
    base64  -d  key.bin.enc  >  key.bin
  6. Finally, you must decrypt the decoded value.

    openssl  rsautl  -decrypt  -inkey privatekey.txt  -in key.bin  -out key.txt

The file named key.txt will contain the base64 encoded secret that should then be used to generate HMAC signatures.

5.2. Signature calculation

A valid signature must be created and included on every request made to the PE API. Likewise, every callback will include a signature and we highly recommend clients validate this signature to ensure data integrity.

The formula for generating a valid signature is as follows:

signature  =  HMAC-SHA512(url  +  sha256(payload),  base64decoded(Secret))

The signature generation process is described in more detail below:

  1. Get the path of the operation minus the hostname. For example, if the operation is to create a new deposit with the URL:

    http://psp/deposits/create
    String url="/deposits/create";
  2. Get the payload as it is, with spaces and tabs etc., and calculate a SHA-256 hash value for it. The results are bytes that can also be expressed as hex.

    String payload = "{\n" +
                    "\t\"accountId\": \"00000000-0000-0000-0000-00000000000\",\n" +
                    "\t\"reference\": \"00000000000000\",\n" +
                    "\t\"callbackUrl\": \"http://localhost:0000/\",\n" +
                    "\t\"expiryDate\": \"2020-01-02T03:04:05.123Z\",\n" +
                    "\t\"requestedAmount\": {\n" +
                    "\t\t\"amount\": 1.00,\n" +
                    "\t\t\"currency\": \"USD\"\n" +
                    "\t},\n" +
                    "\t\"nonce\":1\n" +
                    "}";
    
    String sha256Hex = DigestUtils.sha256Hex(payload);
  3. Concatenate the URL and sha256Hex and apply the HMAC-SHA512 using the secret. Then retrieve the result in hex.

    String computedSignature = url + sha256Hex;
    byte[] keyInBytes = Base64.getDecoder().decode(secretBase64Encoded);
    String signatureHeader = new HmacUtils(HmacAlgorithms.HMAC_SHA_512, keyInBytes).hmacHex(computedSignature);

An example with those values and secret in base64: : bXlzZWNyZXQ=`

sha256HexOfPayload=99ccff6cf3ceba5f571b5b6bc6592156dda97c534af9c67635792cffded7db05
urlPlusSha256HexOfPayload=/deposit/create/99ccff6cf3ceba5f571b5b6bc6592156dda97c534af9c67635792cffded7db05
signature=9cced59ae5987fa669f3fe0ef533df32d1e948e58014327f95402090480e449e3faa3290f37f1ed66bd5ffd053539651826591a7a72666809b7203c9e6eaaf18

5.3. IP Whitelist

Updates to active payments are sent asynchronously by the API via a callbacks mechanism. In order to register for callbacks, we must add the IP addresses of the client(s) where you wish to receive the notifications to a whitelist. You can send the list of IP addresses to [email protected].

5.4. Setup PE accounts

In order to begin using the PE, at least one valid PE account must be setup and activated in Treasury.

You can contact the Customer Support Team if you need more information.

6. Deposits

6.2. Address verification

To enhance security during the deposit process you can use address verification. Address verification creates a digital signature of the address payload and sends it back as a part of the response.

Use the field called 'verificationSignature' to ensure that the address is valid and has not been tampered with. You can find the new field in Appendix B: Callback Samples under 10.B.1 Deposits.

To validate the signature of each address, you need to:

  1. Ask Customer Support for your public key to validate the signatures.

  2. Build the signed payload organizationId#walletId#address.

  3. Verify the signature using the algorithm SHA512withRSA.

Below, you’ll find an example of the verification process in Java:

private boolean isSignatureValid(String organizationId,
                                 String walletId,
                                 String address,
                                 String verificationSignature,
                                 String base64PublicKey) throws GeneralSecurityException {

    var publicKey = convertFromPublicKey(base64PublicKey);
    var payloadBytes = String.join("#", organizationId, walletId, address).getBytes(UTF_8);
    var addressSigningAlgorithm = Signature.getInstance("SHA512withRSA");
    addressSigningAlgorithm.initVerify(publicKey);
    addressSigningAlgorithm.update(payloadBytes);
    return addressSigningAlgorithm.verify(Base64.getDecoder().decode(verificationSignature));
}

private PublicKey convertFromPublicKey(String publicKey) {
    return Try.of(() -> {
        var asymmetricKeyFactory = KeyFactory.getInstance("RSA");
        var X509publicKey = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
        return asymmetricKeyFactory.generatePublic(X509publicKey);
    }).get();
}

8. Account balance

The API exposes an endpoint to query the current balance for an existing PE account. The balance is expressed in both fiat and crypto currency. It also includes the exchange rate that has been applied to calculate the conversion.

8.1. Missing exchange rate

It might happen that the exchange rate is not available at the time the account balance is requested. If this is the case, only the crypto currency based balance will be returned.

8.2. Account balance structure

The balance of an account is defined as follows:

  • Total balance: Confirmed balance + unconfirmed balance.

  • Confirmed balance: Amounts credited from transactions that have at least 1 confirmation on the blockchain.

  • Unconfirmed balance: Amounts credited from transactions that don’t have any confirmation on the blockchain.

  • Available balance: Confirmed balance - locked balance - unconfirmed sent balance + unconfirmed change balance.

  • Locked balance: Amounts and fees from sent orders created but not broadcasted to the blockchain.

  • Unconfirmed sent balance: Amounts and fees from sent orders broadcasted but unconfirmed on the blockchain.

  • Unconfirmed change balance: Amounts credited from unconfirmed transactions paid from that same account.

9. Callbacks

Updates to active payments are sent asynchronously by the API over HTTP via a callbacks mechanism.

The callback payload contains all relevant information associated to a payment. A callback is sent when an update to the state of a payment occurs. This means that the quickest way to be notified of a change to a deposit or payout is to be subscribed to the callback system. A change to the state of a payment may occur in the following scenarios: user actions (e.g. cancel), funds movements (e.g. receive or send funds, confirmation…) or system updates (e.g. expired).

PE tries to guarantee callback delivery via a retry policy. If a successful acknowledgement response is not received on the first attempt, an automatic retry mechanism is initiated that will perform an identical callback until a successful response is received. This retry mechanism is configurable to client needs.

Each callback contains a callbackId and a callbackType. The callbackId is unique, and can be used to identify the callback so in case of retries the client can know if has been already processed or not. The callbackType is a series of values (an enum) that allows the callback to be identified. A full list of callback types can be found in Deposit Callbacks and Payout Callbacks.

Last but not least, each callback includes a signature header built using the formula in Signature Calculation. It is highly recommended clients validate this signature for every callback to ensure data integrity.

9.1. Deposits Callbacks

Callbacks associated to deposits will be initiated once the first request for a payment to be created is successfully received and registered by the system. This request must include all required fields along with a valid signature and nonce.

All the possible deposit callback types are briefly described below:

  • DEPOSIT_CREATED: sent once a new bitcoin address has been generated and a Receive Order has been created.

  • DEPOSIT_CREATION_FAILED: sent if any failures occur during the address & Receive Order creation process, e.g. network issue, database issue, etc.

  • DEPOSIT_CREATION_TIMEOUT: sent if the system takes too long to create a new address or Receive Order. This callback is currently sent if the creation process takes more than 1 minute.

    • From the perspective of the client, this callback must be handled as an HTTP timeout whereby it is not possible to determine if the result was a success or failure.

  • DEPOSIT_RECEIVING_FUNDS: sent when the bitcoin address associated to the deposit receives funds but the funds are not yet confirmed. In bitcoin terms this means that the funds have been detected in the mempool.

  • DEPOSIT_VOID: sent when a user marks the corresponding Receive Order in Treasury as Void.

  • DEPOSIT_EXPIRED: sent when the deposit expires in the system without any payment being made to its corresponding address. Expiry is based on an attribute named expiryTime which is a required field sent by the client at payment creation time.

  • DEPOSIT_COMPLETED: sent when all payments made to the associated bitcoin address are confirmed.

    • If a new payment is sent to the address after the deposit is set to COMPLETED, a new deposit is created with a new unique depositId. Callbacks will then be sent out for for this new deposit.

9.2. Payouts Callbacks

Callbacks associated to payouts will be initiated once the first request for a payment to be created is successfully received and registered by the system. This request must include all required fields along with a valid signature and nonce.

All the possible payout callback types are briefly described below:

  • PAYOUT_CREATED: sent once a new Send Order is successfully created in Treasury.

  • PAYOUT_CREATION_FAILED: sent if any failures occur during the Send Order creation process, e.g. network issue, database issue, etc.

  • PAYOUT_CREATION_TIMEOUT: sent if the system takes too long to create the new Send Order. This callback is currently sent if the creation process takes more than 1 minute.

    • From the perspective of the client, this callback must be handled as an HTTP timeout whereby it is not possible to determine if the result was a success or failure.

  • PAYOUT_REQUIRES_AUTHORIZATIONS: sent if any updates occur to the authorization state of the Send Order (e.g. a user provides a new authorization, or an existing authorization expires) but the payout still requires additional authorizations.

  • PAYOUT_COSIGNER_AUTHORIZED: sent when all the authorizations are provided.

  • PAYOUT_SIGNING: sent once the transaction signing process has started.

  • PAYOUT_SENDING: sent once the transaction broadcasting process has started.

  • PAYOUT_SENT: sent once the Send Order has been broadcast to the bitcoin blockchain. In bitcoin terms this means that funds have been detected in the mempool.

  • PAYOUT_COMPLETED: sent when the blockchain transaction associated to the payout is Confirmed.

  • PAYOUT_CANCELLED: sent if a Treasury user cancels the associated Send Order.

10. Appendices

Appendix A: Code Samples

10.A.1. Generate a valid signature for a request

<?php
    $secretBase64Encoded = '<YOUR_SECRET_BASE64_ENCODED_HERE>';
    $url = '/deposits/create';
    $payload = "{\n" .
                "\t\"accountId\": \"00000000-0000-0000-0000-00000000000\",\n" .
                "\t\"reference\": \”00000000000000\",\n" .
                "\t\"callbackUrl\": \"http://somehost.com\",\n" .
                "\t\"expiryDate\": \"2020-01-02T03:04:05.123Z\",\n" .
                "\t\"requestedAmount\": {\n" .
                "\t\t\"amount\": 1.00,\n" .
                "\t\t\"currency\": \"USD\"\n" .
                "\t},\n" .
                "\t\"nonce\”:1\n" .
                "}";

    echo hash_hmac('sha512', $url . hash('sha256', $payload), base64_decode($secretBase64Encoded));
?>

Appendix B: Callback Samples

10.B.1. Deposits

Deposit created
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"DEPOSIT_CREATED",
  "depositId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "creditOrderAlphanumericId":"AAAAAA",
  "requestedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  },
  "requestedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "fixedExchangeRate":{
    "rate":1.00000000,
    "fromCurrency":"USD",
    "toCurrency":"XBT",
    "measuredDate":"2020-01-02T03:04:05.123Z"
  },
  "depositState":"CREATED",
  "expiryDate":"2020-01-02T03:04:05.123Z",
  "receiverAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "verificationSignature": "+qITVtPMKczDVFW/v1OiQNUQpHEN239O6+jOQYYUuKLFogItgGVe6EQutEUs41VCRUmzk=",
  "receivedFunds":[],
  "totalReceivedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "totalReceivedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  }
}
Deposit created with already used address
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"DEPOSIT_COMPLETED",
  "depositId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "creditOrderAlphanumericId":"AAAAAA",
  "requestedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  },
  "requestedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "fixedExchangeRate":{
    "rate":1.00000000,
    "fromCurrency":"USD",
    "toCurrency":"XBT",
    "measuredDate":"2020-01-02T03:04:05.123Z"
  },
  "depositState":"COMPLETED",
  "expiryDate":"2020-01-02T03:04:05.123Z",
  "receiverAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "verificationSignature": "+qITVtPMKczDVFW/v1OiQNUQpHEN239O6+jOQYYUuKLFogItgGVe6EQutEUs41VCRUmzk=",
  "receivedFunds":[
    {
      "txHash":"000000000000000000000000000000000000000000000",
      "amount":{
        "amount":1.00000000,
        "currency":"XBT"
      },
      "createdDate":"2020-01-02T03:04:05Z",
      "confirmedDate":"2020-01-02T03:04:05Z",
      "state":"CONFIRMED",
      "rate":{
        "rate":1.00000000,
        "fromCurrency":"XBT",
        "toCurrency":"USD",
        "measuredDate":"2020-01-02T03:04:05Z"
      },
      "transactionId": "AAAAAA",
      "confirmations": 1
    }
  ],
  "duplicatedFromDepositId": "00000000-0000-0100-0000-00000000000",
  "totalReceivedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "totalReceivedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  }
}
Deposit creation failed
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"DEPOSIT_CREATION_FAILED",
  "error":{
    "details":"error details"
  },
  "depositId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "requestedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  },
  "requestedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "fixedExchangeRate":{
    "rate":1.00000000,
    "fromCurrency":"USD",
    "toCurrency":"XBT",
    "measuredDate":"2020-01-02T03:04:05.123Z"
  },
  "depositState":"CREATE_FAILED",
  "expiryDate":"2020-01-02T03:04:05Z",
  "receivedFunds":[],
  "totalReceivedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "totalReceivedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  }
}
Deposit creation timeout
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"DEPOSIT_CREATION_TIMEOUT",
  "error":{
    "details":" Timeout creating deposit"
  },
  "depositId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "requestedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  },
  "requestedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "fixedExchangeRate":{
    "rate":1.00000000,
    "fromCurrency":"USD",
    "toCurrency":"XBT",
    "measuredDate":"2020-01-02T03:04:05.123Z"
  },
  "depositState":"CREATE_TIMEOUT",
  "expiryDate":"2020-01-02T03:04:05Z",
  "receivedFunds":[],
  "totalReceivedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "totalReceivedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  }
}
Deposit receiving funds
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"DEPOSIT_RECEIVING_FUNDS",
  "depositId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "creditOrderAlphanumericId":"AAAAAA",
  "requestedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  },
  "requestedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "fixedExchangeRate":{
    "rate":1.00000000,
    "fromCurrency":"USD",
    "toCurrency":"XBT",
    "measuredDate":"2020-01-02T03:04:05.123Z"
  },
  "depositState":"RECEIVING_FUNDS",
  "expiryDate":"2020-01-02T03:04:05.123Z",
  "receiverAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "verificationSignature": "+qITVtPMKczDVFW/v1OiQNUQpHEN239O6+jOQYYUuKLFogItgGVe6EQutEUs41VCRUmzk=",
  "receivedFunds":[
    {
      "txHash":"000000000000000000000000000000000000000000000",
      "amount":{
        "amount":1.00000000,
        "currency":"XBT"
      },
      "createdDate":"2020-01-02T03:04:05Z",
      "state":"UNCONFIRMED",
      "rate":{
        "rate":1.00000000,
        "fromCurrency":"XBT",
        "toCurrency":"USD",
        "measuredDate":"2020-01-02T03:04:05Z"
      },
      "transactionId": "AAAAAA",
      "confirmations": 0
    }
  ],
  "totalReceivedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "totalReceivedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  }
}
Deposit receiving funds - multiple payments
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"DEPOSIT_RECEIVING_FUNDS",
  "depositId":"00000000-0000-0000-0000-00000000000",
  "reference":"123456",
  "creditOrderAlphanumericId":"AAAAAA",
  "requestedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  },
  "requestedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "fixedExchangeRate":{
    "rate":1.00000000,
    "fromCurrency":"USD",
    "toCurrency":"XBT",
    "measuredDate":"2020-01-02T03:04:05.123Z"
  },
  "depositState":"RECEIVING_FUNDS",
  "expiryDate":"2020-01-02T03:04:05.123Z",
  "receiverAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "verificationSignature": "+qITVtPMKczDVFW/v1OiQNUQpHEN239O6+jOQYYUuKLFogItgGVe6EQutEUs41VCRUmzk=",
  "receivedFunds":[
    {
      "txHash":"000000000000000000000000000000000000000000000",
      "amount":{
        "amount":1.00000000,
        "currency":"XBT"
      },
      "createdDate":"2020-01-02T03:04:05Z",
      "state":"CONFIRMED",
      "rate":{
        "rate":1.00000000,
        "fromCurrency":"XBT",
        "toCurrency":"USD",
        "measuredDate":"2020-01-02T03:04:05Z"
      },
      "transactionId": "AAAAAA",
      "confirmations": 1
    },
    {
      "txHash":"000000000000000000000000000000000000000000000",
      "amount":{
        "amount":1.00000000,
        "currency":"XBT"
      },
      "createdDate":"2020-01-02T03:04:05Z",
      "state":"UNCONFIRMED",
      "rate":{
        "rate":1.00000000,
        "fromCurrency":"XBT",
        "toCurrency":"USD",
        "measuredDate":"2020-01-02T03:04:05Z"
      },
      "transactionId": "AAAAAA",
      "confirmations": 0
    }
  ],
  "totalReceivedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "totalReceivedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  }
}
Deposit void
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"DEPOSIT_VOID",
  "depositId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "creditOrderAlphanumericId":"AAAAAA",
  "requestedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  },
  "requestedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "fixedExchangeRate":{
    "rate":1.00000000,
    "fromCurrency":"USD",
    "toCurrency":"XBT",
    "measuredDate":"2020-01-02T03:04:05.123Z"
  },
  "depositState":"VOID",
  "expiryDate":"2020-01-02T03:04:05.123Z",
  "receiverAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "verificationSignature": "+qITVtPMKczDVFW/v1OiQNUQpHEN239O6+jOQYYUuKLFogItgGVe6EQutEUs41VCRUmzk=",
  "receivedFunds":[],
  "totalReceivedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "totalReceivedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  }
}
Deposit expired
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"DEPOSIT_EXPIRED",
  "depositId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "creditOrderAlphanumericId":"AAAAAA",
  "requestedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  },
  "requestedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "fixedExchangeRate":{
    "rate":1.00000000,
    "fromCurrency":"USD",
    "toCurrency":"XBT",
    "measuredDate":"2020-01-02T03:04:05.123Z"
  },
  "depositState":"VOID",
  "expiryDate":"2020-01-02T03:04:05.123Z",
  "receiverAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "verificationSignature": "+qITVtPMKczDVFW/v1OiQNUQpHEN239O6+jOQYYUuKLFogItgGVe6EQutEUs41VCRUmzk=",
  "receivedFunds":[],
  "totalReceivedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "totalReceivedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  }
}
Deposit completed
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"DEPOSIT_COMPLETED",
  "depositId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "creditOrderAlphanumericId":"AAAAAA",
  "requestedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  },
  "requestedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "fixedExchangeRate":{
    "rate":1.00000000,
    "fromCurrency":"USD",
    "toCurrency":"XBT",
    "measuredDate":"2020-01-02T03:04:05.123Z"
  },
  "depositState":"COMPLETED",
  "expiryDate":"2020-01-02T03:04:05.123Z",
  "receiverAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "verificationSignature": "+qITVtPMKczDVFW/v1OiQNUQpHEN239O6+jOQYYUuKLFogItgGVe6EQutEUs41VCRUmzk=",
  "receivedFunds":[
    {
      "txHash":"000000000000000000000000000000000000000000000",
      "amount":{
        "amount":1.00000000,
        "currency":"XBT"
      },
      "createdDate":"2020-01-02T03:04:05Z",
      "confirmedDate":"2020-01-02T03:04:05Z",
      "state":"CONFIRMED",
      "rate":{
        "rate":1.00000000,
        "fromCurrency":"XBT",
        "toCurrency":"USD",
        "measuredDate":"2020-01-02T03:04:05Z"
      },
      "transactionId": "AAAAAA",
      "confirmations": 1
    }
  ],
  "totalReceivedAmountInCrypto":{
    "amount":1.00000000,
    "currency":"XBT"
  },
  "totalReceivedAmountInFiat":{
    "amount":1.00,
    "currency":"USD"
  }
}

10.B.2. Payouts

Payout created
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"PAYOUT_CREATED",
  "payoutId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "debitOrderAlphanumericId":"AAAAAA",
  "requestedAmount":{
    "amount":1.00,
    "currency":"USD"
  },
  "payoutState":"CREATED",
  "destinationAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "feePolicy": "NORMAL",
  "subtractFee": false,
  "useCoinConsolidation" : true
}
Payout creation failed
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"PAYOUT_CREATION_FAILED",
  "error":{
    "details":"error details"
  },
  "payoutId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "requestedAmount":{
    "amount":1.00,
    "currency":"USD"
  },
  "payoutState":"CREATE_FAILED",
  "destinationAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "feePolicy": "NORMAL",
  "subtractFee": false,
  "useCoinConsolidation" : true
}
Payout creation timeout
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"PAYOUT_CREATION_TIMEOUT",
  "error":{
    "details":"Timeout creating payout"
  },
  "payoutId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "requestedAmount":{ "amount":1.00, "currency":"USD" },
  "payoutState":"CREATE_TIMEOUT",
  "destinationAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "feePolicy": "NORMAL",
  "subtractFee": false,
  "useCoinConsolidation" : true
}
Payout requires authorizations
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"PAYOUT_REQUIRES_AUTHORIZATIONS",
  "payoutId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "debitOrderAlphanumericId":"AAAAAA",
  "requestedAmount":{
    "amount":1.00,
    "currency":"USD"
  },
  "payoutState":"COSIGNER_AUTHORIZATIONS_PENDING",
  "destinationAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "authorization":{
    "id":"00000000-0000-0000-0000-00000000000",
    "state":"PENDING",
    "cosignerAuthorizations":[
      {
        "state":"AUTHORIZED",
        "userGroupName":"us.authorizers.psp",
        "username":"automation000.test",
        "expire":"2020-01-02T03:04:05.123Z"
      },
      {
        "state":"PENDING",
        "userGroupName":"us.authorizers.treasury"
      }
    ]
  },
  "feePolicy": "NORMAL",
  "subtractFee": false,
  "useCoinConsolidation" : true
}
Payout cancelled
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"PAYOUT_CANCELLED",
  "payoutId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "debitOrderAlphanumericId":"AAAAAA",
  "requestedAmount":{"amount":1.00, "currency":"USD"},
  "payoutState":"CANCELLED",
  "destinationAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "authorization":{
    "id":"00000000-0000-0000-0000-00000000000",
    "state":"PENDING",
    "cosignerAuthorizations":[
      {
        "state":"EXPIRED",
        "userGroupName":"us.authorizers.psp",
        "username":"automation000.test",
        "expire":"2020-01-02T03:04:05.123Z"
      },
      {
        "state":"PENDING",
        "userGroupName":"us.authorizers.treasury"
      }
    ]
  },
  "feePolicy": "NORMAL",
  "subtractFee": false,
  "useCoinConsolidation" : true
}
Payout cosigner authorized
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"PAYOUT_COSIGNER_AUTHORIZED",
  "payoutId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "debitOrderAlphanumericId":"AAAAAA",
  "requestedAmount":{
    "amount":1.00,
    "currency":"USD"
  },
  "payoutState":"AUTHORIZED",
  "destinationAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "authorization":{
    "id":"00000000-0000-0000-0000-00000000000",
    "state":"FULLY_AUTHORIZED",
    "cosignerAuthorizations":[
      {
        "state":"AUTHORIZED",
        "userGroupName":"us.authorizers.psp",
        "username":"automation000.test",
        "expire":"2020-01-02T03:04:05.123Z"
      }
    ]
  },
  "feePolicy": "NORMAL",
  "subtractFee": false,
  "useCoinConsolidation" : true
}
Payout signing
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"PAYOUT_SIGNING",
  "payoutId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "debitOrderAlphanumericId":"AAAAAA",
  "requestedAmount":{
    "amount":1.00,
    "currency":"USD"
  },
  "payoutState":"SIGNING",
  "destinationAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "authorization":{
    "id":"00000000-0000-0000-0000-00000000000",
    "state":"FULLY_AUTHORIZED",
    "cosignerAuthorizations":[
      {
        "state":"AUTHORIZED",
        "userGroupName":"us.authorizers.psp",
        "username":"automation000.test",
        "expire":"2020-01-02T03:04:05.123Z"
      }
    ]
  },
  "feePolicy": "NORMAL",
  "subtractFee": false,
  "useCoinConsolidation" : true
}
Payout sending
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"PAYOUT_SENDING",
  "payoutId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "debitOrderAlphanumericId":"AAAAAA",
  "requestedAmount":{
    "amount":1.00,
    "currency":"USD"
  },
  "payoutState":"SENDING",
  "destinationAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "authorization":{
    "id":"00000000-0000-0000-0000-00000000000",
    "state":"FULLY_AUTHORIZED",
    "cosignerAuthorizations":[
      {
        "state":"AUTHORIZED",
        "userGroupName":"us.authorizers.psp",
        "username":"automation000.test",
        "expire":"2020-01-02T03:04:05.123Z"
      }
    ]
  },
  "feePolicy": "NORMAL",
  "subtractFee": false,
  "useCoinConsolidation" : true
}
Payout sent
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"PAYOUT_SENT",
  "payoutId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "debitOrderAlphanumericId":"AAAAAA",
  "requestedAmount":{
    "amount":1.00,
    "currency":"USD"
  },
  "payoutState":"SENT",
  "destinationAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "sentFunds":{
    "txHash":"000000000000000000000000000000000000000000000",
    "amount":{
      "amount":1.00000000,
      "currency":"XBT"
    },
    "fee":{
      "amount":1.00000000,
      "currency":"XBT"
    },
    "createdDate":"2020-01-02T03:04:05Z",
    "state":"UNCONFIRMED",
    "rate":{
      "rate":1.00000000,
      "fromCurrency":"XBT",
      "toCurrency":"USD",
      "measuredDate":"2020-01-02T03:04:05Z"
    },
    "transactionId": "AAAAAA",
    "confirmations": 0
  },
  "authorization":{
    "id":"00000000-0000-0000-0000-00000000000",
    "state":"FULLY_AUTHORIZED",
    "cosignerAuthorizations":[
      {
        "state":"AUTHORIZED",
        "userGroupName":"us.authorizers.psp",
        "username":"automation000.test",
        "expire":"2020-01-02T03:04:05.123Z"
      }
    ]
  },
  "feePolicy": "NORMAL",
  "subtractFee": false,
  "useCoinConsolidation" : true
}
Payout completed
{
  "callbackId":"00000000-0000-0000-0000-00000000000",
  "callbackType":"PAYOUT_COMPLETED",
  "payoutId":"00000000-0000-0000-0000-00000000000",
  "reference":"00000000000000",
  "debitOrderAlphanumericId":"AAAAAA",
  "requestedAmount":{
    "amount":1.00,
    "currency":"USD"
  },
  "payoutState":"COMPLETED",
  "destinationAddress":"A11aAAa111A1Aa1aAAa111aaA1A11A1aAaAAA1aaA1Aaa1A11A11A1AAA1A1AaaA",
  "sentFunds":{
    "txHash":"000000000000000000000000000000000000000000000",
    "amount":{
      "amount":1.00000000,
      "currency":"XBT"
    },
    "fee":{
      "amount":1.00000000,
      "currency":"XBT"
    },
    "createdDate":"2020-01-02T03:04:05Z",
    "confirmedDate":"2020-01-02T03:04:05Z",
    "state":"CONFIRMED",
    "rate":{
      "rate":1.00000000,
      "fromCurrency":"XBT",
      "toCurrency":"USD",
      "measuredDate":"2020-01-02T03:04:05Z"
    },
    "transactionId": "AAAAAA",
    "confirmations": 1
  },
  "authorization":{
    "id":"00000000-0000-0000-0000-00000000000",
    "state":"FULLY_AUTHORIZED",
    "cosignerAuthorizations":[
      {
        "state":"AUTHORIZED",
        "userGroupName":"us.authorizers.psp",
        "username":"automation000.test",
        "expire":"2020-01-02T03:04:05.123Z"
      }
    ]
  },
  "feePolicy": "NORMAL",
  "subtractFee": false,
  "useCoinConsolidation" : true
}

Appendix C: Platform Error Code

The most common errors are described here.

10.C.1. 4XX Client error

401 Unauthorized
HTTP Response Code Platform Error Code Description

401

INVALID_HMAC_SIGNATURE

The signature header is not valid

404 Not found
HTTP Response Code Platform Error Code Description

404

NOT_FOUND_ACCOUNT

The Account was not found in the system

404

NOT_FOUND_XRATE

There is not Exchange Rate information for the specified FIAT currency and XBT

404

NOT_FOUND_DEPOSIT

The Deposit was not found in the system

404

NOT_FOUND_PAYOUT

The Payout was not found in the system

404

NOT_FOUND_CLIENT_TOKEN

Client token not setup

404

NOT_FOUND_CLIENT

The key provided as header identifying the client was not found in the system

404

NOT_FOUND_ACCOUNT_BALANCE

Account balance not found for the requested accountId

409 Conflict
HTTP Response Code Platform Error Code Description

409

CONFLICT_INVALID_NONCE

The nonce in the header was not valid

409

CONFLICT_VALIDATING_MFA_ACCESS_CODE

The OTP provided by the user was not valid

409

CONFLICT_EXPIRED_XRATE

The current Exchange Rate information in the system was expired

409

CONFLICT_PAYOUT_IN_WRONG_STATE

The Payout was not in the expected status

409

CONFLICT_PAYOUT_ALREADY_FULLY_AUTHORIRIZED

The Payout was already fully authorized

409

CONFLICT_PAYOUT_CANNOT_AUTHORIRIZE_VIA_USER

The user with username cannot authorize the Payout

409

CONFLICT_USER_ALREADY_AUTHORIZED

The user with username already provided a valid authorization

409

CONFLICT_ACCOUNT_BALANCE_NOT_ENOUGH

Not enough available balance for the operation

409

CONFLICT_CUSTOM_FEE_RATE_DISABLED

When a custom fee rate is set but the functionality is disabled

422 Unprocessable entity
HTTP Response Code Platform Error Code Description

422

INVALID_CURRENCY

The currency specified and the currency of the account don’t match

422

INVALID_ACCOUNT

The Account was not valid

422

INVALID_DEPOSIT_STATE

The Deposit was in an invalid state

422

INVALID_PAYOUT_STATE

The Payout was in an invalid state

422

INVALID_QUERY_DATE

The queryDate was not valid. It should be in a range within the arrival time to the server +/- 2 min

422

INVALID_CUSTOM_FEE_RATE_TOO_HIGH

When a requested custom fee rate exceeds the calculated maximum rate

422

INVALID_CUSTOM_FEE_RATE_TOO_LOW

When a requested custom fee rate is lower than the minimum rate allowed

10.C.2. 5XX Server Error

500 Internal server error
HTTP Response Code Platform Error Code Description

500

ERROR_UNKNOWN_CREDIT_ORDER_STATE

Internal error mapping credit state to deposit state

500

ERROR_UNKNOWN_DEBIT_ORDER_STATE

Internal error mapping debit state to payout state

500

ERROR_RETRIEVING_TOKEN_DATA

Internal error fetching the token

500

ERROR_CALCULATING_ACCOUNT_BALANCE

Internal error calculating account balance

502 Bad gateway
HTTP Response Code Platform Error Code Description

502

ERROR_GETTING_CREDIT_ORDER

Internal error fetching credit information

502

ERROR_INVALID_AUTH_TOKEN_SIGNATURE

Internal error authentication

502

ERROR_IN_TOKEN_SERVICE

Internal error reading/writing the token