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.
The base url for all requests is
https://api.stablehouse.io/v1
.The complete API Reference is accessible through our Swagger UI here. The raw Swagger JSON document is available here.
While it's possible to use the API with plain HTTP calls, we recommend that you use a Swagger code generation tool like https://github.com/RSuter/NSwag to generate your API Client.
All the API responses are wrapped into a common JSON object.
Most endpoints use HTTP POST with a request body. The request body must always be JSON, and the response body will always be JSON. URL-based parameters, including path parameter and query string parameters, are not used in 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:
- ApiKey
- Secret
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:
Create a SHA256 HMAC against your UTF8 encoded secret.
Build a string that contains
timestamp + method + requestPath + body
. Thetimestamp
should be Unix Epoch seconds (UTC). Themethod
is the HTTP method and should be upper case. TherequestPath
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). Thebody
is the request body string or omitted if there is no request body.Compute the hash for this string using the SHA256 HMAC you created on step 1.
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
.
You then continue by generating the string to sign:
It's1550248260POST/v1/deposits/get-address{"CurrencyCode":"TUSD"}
You sign this string using your Secret. You get the following signature:
84c3d5a0d516a5080b3f8ac8b14ba4d55006e73c4d62c05adce2366399454982
Finally, you add those 3 headers to your request.