NAV Navbar
.NET Core JSON Python SH

Introduction

Welcome to the Stablehouse's developer documentation.

{
"result": ...,
"isSuccessful": true,
"errorMessage": null,
}

This document explains the general guidelines about how to the Stablehouse API.

HTTP Status Codes

HTTP status codes are limited to 200, 400 (Bad Request), 401 (Unauthorized), 404 (Not Found) and 429 (Rate Limit Exceeded). HTTP 500 and above may be returned when the service is down for maintaince or when an unexpected error occurs.

HTTP 200

This indicates the specified action was successful.

HTTP 400

This indicates that the request failed because the parameter a user specified was invalid. This could be a datatype issue, such as specifying a non-numeric string in a anumeric field, or because of a higher level rejection, such as insufficent funds to execute a trade.

HTTP 429

This indicates the request failed because of exceeded rate limits. In that situation, the following headers will be set on the response:

Header Description
X-Rate-Limit-Limit The maximum amount of requests that can be issued in the time window.
X-Rate-Limit-Remaining The amount of requests that can be issued within the rate limit budget.
X-Rate-Limit-Reset A ISO 8601 compliant timestamp of when requests will be allowed.

Authentication

Generating an API Key

You must create an API key through Stablehouse's website. Upon creating a key you will be given the following items:

The Key and Secret will be randomly generated and provided to you by Stablehouse.
In addition to those keys, you can provide a Passphrase to further secure your API access.
Stablehouse stores the salted bcrypt hash of your passphrase for verification.

Authenticating

curl -X POST \
  https://api.stablehouse.io/api/funds/get-deposit-address \
  -H 'Accept: text/plain, application/json, text/json' \
  -H 'Content-Type: application/json' \
  -H 'SH-API-KEY: yDC2HdqvenXQdLQMaq6h62b27P41JqS0LRVT+iuL/CQ=' \
  -H 'SH-SIGNATURE: 84c3d5a0d516a5080b3f8ac8b14ba4d55006e73c4d62c05adce2366399454982' \
  -H 'SH-TIMESTAMP: 1550248260' \
  -H 'cache-control: no-cache' \
  -d '{"CurrencyCode":"TUSD"}'
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace StablehouseExample
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string key = "MY_KEY";
            string secret = "MY_SECRET";
            string passphrase = "MY_PASS";
            string baseUrl = "https://api.stablehouse.io/v1";

            var handler = new StablehouseApiAuthenticationHandler(key, secret, passphrase)
            {
                InnerHandler = new HttpClientHandler()
            };

            var client = new StablehouseClient(handler, true)
            {
                BaseAddress = new Uri(baseUrl)
            };

            var accountId = await client.WhoAmI(CancellationToken.None);
            Console.WriteLine($"My account ID: {accountId}");
        }
    }

    public class StablehouseClient : HttpClient
    {
        public StablehouseClient(DelegatingHandler handler, bool disposeHandler) : base(handler, disposeHandler)
        {
            DefaultRequestHeaders
                .Accept
                .Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json));
        }

        public async Task<string> WhoAmI(CancellationToken cancellationToken)
        {
            var httpResponseMessage = await PostAsync("/v1/system/whoami", null, cancellationToken);
            httpResponseMessage.EnsureSuccessStatusCode();
            var content = await httpResponseMessage.Content.ReadAsStringAsync();
            var response = JsonConvert.DeserializeObject<StablehouseResponse<string>>(content);
            return response.Result;
        }
    }

    public class StablehouseResponse<T>
    {
        public T Result { get; set; }
        public bool IsSuccessful { get; set; }
        public string ErrorMessage { get; set; }
    }

    public class StablehouseApiAuthenticationHandler : DelegatingHandler
    {
        private readonly string _apiKey;
        private readonly HMACSHA256 _hmacSha256;
        private readonly string _stableHousePassphrase;

        public StablehouseApiAuthenticationHandler(string apiKey, string apiSecret, string stableHousePassphrase)
        {
            _apiKey = apiKey;
            _hmacSha256 = new HMACSHA256(Encoding.UTF8.GetBytes(apiSecret));
            _stableHousePassphrase = stableHousePassphrase;
        }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var unixTimeStamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();

            var method = request.Method.Method.ToUpperInvariant();
            var requestPath = request.RequestUri.PathAndQuery;
            var requestBody = request.Content is null ? string.Empty : await request.Content?.ReadAsStringAsync();
            var what = unixTimeStamp + method + requestPath + requestBody;

            byte[] signatureBytes;
            lock (_hmacSha256)
                signatureBytes = _hmacSha256.ComputeHash(Encoding.UTF8.GetBytes(what));

            var signature = ToHexString(signatureBytes);
            request.Headers.Add("SH-API-KEY", _apiKey);
            request.Headers.Add("SH-SIGNATURE", signature);
            request.Headers.Add("SH-TIMESTAMP", unixTimeStamp.ToString("G29"));

            if (!string.IsNullOrWhiteSpace(_stableHousePassphrase))
                request.Headers.Add("SH-PASSPHRASE", _stableHousePassphrase);

            return await base.SendAsync(request, cancellationToken);
        }

        private static string ToHexString(byte[] bytes)
        {
            var sb = new StringBuilder();
            foreach (var t in bytes)
                sb.Append(t.ToString("X2"));
            return sb.ToString();
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            _hmacSha256?.Dispose();
        }
    }
}
# Requires python-requests. Install with pip:
#
#   pip install requests
import json, hmac, hashlib, time, requests, base64
from requests.auth import AuthBase

# Create custom authentication for Stablehouse
class StablehouseExchangeAuth(AuthBase):
    def __init__(self, api_key, secret_key, pass_phrase):
        self.api_key = api_key
        self.secret_key = secret_key
        self.pass_phrase = pass_phrase

    def __call__(self, request):
        timestamp = str(int(time.time()))
        queryString = ''
        finalBody = request.body or ''
        what = timestamp + request.method + request.path_url + queryString + finalBody
        signature = hmac.new(bytes(self.secret_key, 'UTF-8'), str(what).encode('utf-8'), hashlib.sha256)
        signature_hexed = signature.hexdigest().upper()

        request.headers.update({
            'SH-API-KEY': self.api_key,
            'SH-SIGNATURE': signature_hexed,
            'SH-TIMESTAMP': timestamp,
            'SH-PASSPHRASE': self.pass_phrase,
        })
        return request

def printResults(r):
    print('\r\n\r\n')
    print('================================================================================')
    print(' DISPLAYING RESULTS')
    print('================================================================================')
    print('\r\n')
    print('response status code = ' + str(r.status_code) + '\r\n')
    print('response raw text = ' + r.text + '\r\n')
    json_data = json.loads(r.text)
    if json_data['isSuccessful']:
        json_result_field = json_data['result']
        print('json_result_field = ' + str(json_result_field)  + '\r\n')
    else:
        json_error_message_field = json_data['errorMessage']
        print('json_error_message_field = ' + str(json_error_message_field)  + '\r\n')
    print('--------------------------------------------------------------------------------')

# add your api Key here
API_KEY = ''

# add your api Secret here
API_SECRET = ''

# add your api Passphrase here
API_PASSPHRASE = ''

api_url = 'https://api.stablehouse.io'
auth = StablehouseExchangeAuth(API_KEY, API_SECRET, API_PASSPHRASE)

# Get funds
r = requests.post(api_url + '/api/funds/get-funds', auth=auth)
printResults(r)
#{
#  "result": {
#    "funds": [
#      {
#        "currencyCode": "string",
#        "currencyName": "string",
#        "currencyDecimals": 0,
#        "amount": "string",
#        "lockedAmount": "string",
#        "canMarketMake": true
#      }
#    ]
#  },
#  "isSuccessful": true,
#  "errorMessage": "string"
#}

To be authenticated, all requests must contain the following headers:

Header Description
SH-API-KEY The api key as a string
SH-SIGNATURE The hex representation of the signature
SH-TIMESTAMP A timestamp for your request (UTC UNIX timestamp in seconds)

If the API Key was created using a Passphrase, the request must contain the following additional header:

Header Description
SH-PASSPHRASE The passphrase that you provided when creating the API Key

To generate the SH-SIGNATURE header:

  1. Create a SHA256 HMAC against your UTF8 encoded secret.

  2. Build a string that contains timestamp + method + requestPath + body. The timestamp should be Unix Epoch seconds (UTC). The method is the HTTP method and should be upper case. The requestPath is the part of the URL after the domain. requestPath should include the leading forward-slash, and should include the query string if one exists (i.e. the full path). The body is the request body string or omitted if there is no request body.

  3. Compute the hash for this string using the SHA256 HMAC you created on step 1.

  4. HEX-encode the output. This string is case insensitive. The result is the SH-SIGNATURE header.

Example:

You want to query POST https://api.stablehouse.io/v1/deposits/get-address with the following body {"CurrencyCode":"TUSD"}.

Your API Key is yDC2HdqvenXQdLQMaq6h62b27P41JqS0LRVT+iuL/CQ= and your Secret is ZO7jwHpr2a3eVUAASs6xNC7j/NpANUhVvjJbwANGsjM=.

You start by computing the current unix timestamp (this should be the unix epoch seconds UTC). We will suppose it's 1550248260.

  1. You then continue by generating the string to sign:
    It's 1550248260POST/v1/deposits/get-address{"CurrencyCode":"TUSD"}

  2. You sign this string using your Secret. You get the following signature:
    84c3d5a0d516a5080b3f8ac8b14ba4d55006e73c4d62c05adce2366399454982

  3. Finally, you add those 3 headers to your request.