API Key

API Key authentication should only be used to access your own account. If your application requires access to other Shipl users’ accounts, do not use API Key.

Signing requests

API key authentication requires each request to be signed (enhanced security measure). You can create and activate new API keys in in the Shipl Console. Your API keys are assigned to access to testnet (kovan, ropsten, rinkeby) or to livenet (mainnet, poa and xDai).

Your API keys carry many privileges, so be sure to keep them secure! Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth.

Making a request

All request requires three headers: date, authorization and signature. If the HTTP request contains a body, the content-length and content-type headers are also required.

  • The date header is a standard RFC-822 (updated in RFC-1123) date, as per RFC-7231.

  • The authorization header is a standard as per RFC-2617 that, confusingly, is designed for authentication and not authorization. It should contain a string representation of the client's API key.

  • The signature header contains the signature of the entire request, as well as a reference to the version of the protocol, and the algorithm used to generate the signature.

A correctly signed HTTP request should look like this:

POST https://api.shipl.co/orders/order
content-type: application/json
content-length: 90
date: Tue, 20 Apr 2016 18:48:24 GMT
authorization: api-key YOUR_API_KEY
signature: shipl-hmac-auth sha384 64b0a4bd0cbb45c5b2fe8b1e4a15419b6018a9a90eb19046247af6a9e8896bd3

Signature

To calculate the signature, your first needs to create a string representation of the request. When our server receives an authenticated request it computes the the signature and compares it with the signature that you provided. Therefore, the you must create a string representation of the request in the exact same way as the server. This is called "canonicalization."

The format of a canonical representation of a request is:

HTTP Verb + \n
URI + \n
Canonical query string + \n
Canonically formatted signed headers + \n
Hashed body payload

Component

Format

Example

HTTP Verb

upperCase(verb)

POST, GET or DELETE

URI

encode(uri)

/items/test%20item

Query String

encode(paramA) + '=' + encode(valueA) + '&' + encode(paramB) + '=' + encode(valueB)

paramA=valueA&paramB=value%20B

Headers

lowerCase(keyA) + ':' + trim(valueA) + '\n' + lowerCase(keyB) + ':' + trim(valueB)

keyA:valueA keyB:value%20B

Hashed payload

hex(hash('sha256', bodyData))

The HTTP verb must be upper case. The URI should be url-encoded. The query string elements should be alphabetically sorted. The header keys must all be lower case (as per RFC-2616) and alphabetically sorted. The only headers included in the signature should be: authorization and date- however content-length and content-type should be included if the HTTP body is not empty. The last line of the request string should be a hex representation of a SHA384 hash of the request body. If there is no request body, it should be the hash of an empty string.

Programmatically:

upperCase(method) + \n
path + \n
encode(paramA) + '=' + escape(valueA) + '&' + escape(paramB) + '=' + escape(valueB) + \n
lowerCase(headerKeyA) + ':' + trim(headerValueA) + \n + lowerCase(headerKeyB) + ':' + trim(headerKeyB) + \n
hex(hash('sha384', bodyData)) + \n

For Example

POST
/orders/order
paramA=valueA&paraB=value%20B
authorization: api-key YOUR_API_KEY
content-length:15
content-type: application/json
date:Tue, 20 Apr 2016 18:48:24 GMT
8eb2e35250a66c65d981393c74cead26a66c33c54c4d4a327c31d3e5f08b9e1b

Then the HMAC signature of the entire request is generated by signing it with the secret, as a hex representation:

const signature = hex(hmacSha256(secret, requestString))

That value is then sent as the contents of the signature header along with the algorithm used to generate it, as well as the version of the protocol the signature implements.

// protocol version + ' ' + algorithm + ' ' + signature;
headers[signature] = 'shipk-hmac-auth sha384 ' + signature

Usage

Node.js example
Python Example
'use strict'
const axios = require('axios')
const crypto = require('crypto')
const canonicalize = require('../src/nodejs/canonicalize')
const body = {
'metaNonce': '0x9',
'metaSignedTx': 'f9022980843b9a3180b90204bc425976cabe699ec8292f95c3eb9555a01c8080',
'blockchain': 'rinkeby',
'appId': 'YOUR_SHIPL_APP_ID',
'jsonRpcReponse': true,
'id': 1555341488002065
}
const shiplConfig = {
API_KEY: 'YOUR_SHIPL_API_KEY_ID',
API_SECRET: 'YOUR_SHIPL_API_KEY_SECRET',
HOST_URL: 'https://testnet.api.shipl.co'
}
const buildSign = (data, config) => {
return crypto.createHmac('sha384', config.API_SECRET).update(data).digest('hex')
}
const privateRequest = async ({ httpMethod, path, queryString, body }) => {
const dataQueryString = qs.stringify(queryString)
const headers = {
Authorization: `api-key ${shiplConfig.API_KEY}`,
date: new Date().toUTCString()
}
const canonical = canonicalize(httpMethod, path, queryString, headers, JSON.stringify(body))
const signature = buildSign(canonical, shiplConfig)
const requestConfig = {
method: httpMethod,
url: shiplConfig.HOST_URL + path + '?' + dataQueryString,
headers,
data: body
}
requestConfig.headers.signature = 'shipl-hmac-auth sha-384 ' + signature
try {
const response = await axios(requestConfig)
console.log(response.data)
return response
} catch (error) {
console.log(error.response.data)
throw error
}
}
privateRequest({ httpMethod: 'POST', path: '/orders/order', queryString: '', body })
# Requires python-requests.
# Install with pip: pip install requests
# or, with easy-install: easy_install requests
import json, hmac, hashlib, time, requests, base64
from requests.auth import AuthBase
# Create custom authentication for Exchange
class ShiplAuth(AuthBase):
def __init__(self, api_key, secret_key, params):
self.api_key = api_key
self.secret_key = secret_key
self.params = params
def __call__(self, request):
timestamp = str(time.time())
request.headers.update({
'date': timestamp,
'Authorization': 'api-key ' + self.api_key
})
def canonicalize(method, uri, queryString, headers, data):
headerWhitelist = [
'authorization',
'date',
'content-length',
'content-type'
]
method = method.upper()
if (queryString == None or len(queryString) == 0):
queryString = ''
if (data == None or len(queryString) == 0):
data = ''
headerString = ''
for index, key in enumerate(sorted(headers)):
key = key.lower()
if (key not in headerWhitelist):
continue
if (key == 'content-length' and headers[key] == '0'):
continue
headerString += key + ':' + headers[key].strip()
if (index != (len(headers) - 1)):
headerString += '\n'
string = method + '\n'
string += uri + '\n'
string += queryString + '\n'
string += headerString + '\n'
string += data
return string
canonicalRequest = canonicalize(request.method, '/' + request.path_url, (self.params or ''), request.headers, (request.body or ''))
signature = hmac.new(bytes(self.secret_key, 'utf-8'), bytes(canonicalRequest, 'utf-8'), hashlib.sha384).hexdigest()
request.headers.update({ 'signature': 'shipl-hmac-auth sha-384 ' + signature })
return request
api_url = 'https://testnet.api.shipl.co'
api_key = 'YOUR_SHIPL_API_KEY_ID'
api_secret = 'YOUR_SHIPL_API_KEY_SECRET'
params = ''
auth = ShiplAuth(api_key, api_secret, '')
# Get accounts
r = requests.get(api_url + '/orders/order', auth=auth)