Introduction
Welcome to the CoinRPC documentation.
API
Each wallet can be configured to enable API access. See each individual network's API documention for the endpoint URL and the specific methods available.
The API uses the JSON-RPC 2.0 specification.
Authentication
The following headers must be sent with each API request to properly authenticate:
x-api-key
: Your API keyx-signature
: A locally generated signature (see below).x-timestamp
: The unix timestamp of when the request was sent.
Generating the signature:
- Join a dumped string of the JSON request data and the request unix timestamp with a semicolon (:).
- Create an HMAC digest string (hex), using the joined string as the message, your API secret as the key, and SHA256 as the digest method.
- Python
- PHP
def create_signature(payload, secret, timestamp):
import hashlib
import hmac
import json
message = ":".join([json.dumps(payload, separators=(',',':')), str(timestamp)])
return hmac.new(secret.encode('utf-8'), msg=message.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()
function create_signature($payload, $secret, $timestamp) {
$message = implode(":", [json_encode($payload), $timestamp]);
return hash_hmac("sha256", $message, $secret);
}
Request
Requests must be sent using the POST
method with the content-type
header set to application/json
.
The request JSON should be in the following format. The params
member can be omitted if the method does not require any.
{
"id": 1,
"jsonrpc": "2.0",
"method": "get_balance",
"params": {
"currency": "BTC",
}
}
Response
The response will be in JSON format. If an error
member is present, there was an error with the invocation.
- Success
- Error
{
"id": 1,
"jsonrpc": "2.0",
"result": "0.135", // can be any JSON literal, object or list.
}
{
"id": 1,
"jsonrpc": "2.0",
"error": {
"message": "Something went wrong.",
}
}
Client Library
- Python
- PHP
import hashlib
import hmac
import json
import time
import requests
class ApiClient():
def __init__(self, endpoint, api_key, secret):
self.endpoint = endpoint
self.api_key = api_key
self.secret = secret
self.session = requests.Session()
@staticmethod
def create_signature(payload, secret, timestamp):
message = ":".join([json.dumps(payload, separators=(',',':')), str(timestamp)])
return hmac.new(secret.encode('utf-8'), msg=message.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()
def send(self, method, params=None):
payload = {
'id': '1',
'jsonrpc': '2.0',
'method': method,
}
if params:
payload['params'] = params
timestamp = str(int(time.time()))
signature = self.create_signature(payload, self.secret, timestamp)
headers = {
'x-api-key': self.api_key,
'x-signature': signature,
'x-timestamp': timestamp,
}
r = self.session.post(self.endpoint, json=payload, headers=headers)
r.raise_for_status()
res = r.json()
if 'error' in res:
raise Exception(res['error'].get('message'))
return res.get('result')
# configuration
endpoint = 'http://api.coinrpc.com/bitcoin'
api_key = 'YOUR_API_KEY'
api_secret = 'YOUR API SECRET'
# create an instance
client = ApiClient(endpoint, api_key, api_secret)
# make a call to get the wallet balance
try:
result = client.send('get_balance', {'currency': 'BTC'})
print(result)
except Exception as e:
print("error: " + str(e))
class ApiClient {
public function __construct($endpoint, $api_key, $secret) {
$this->endpoint = $endpoint;
$this->api_key = $api_key;
$this->secret = $secret;
}
public static function create_signature($payload, $secret, $timestamp) {
$message = implode(":", [json_encode($payload), $timestamp]);
return hash_hmac("sha256", $message, $secret);
}
public function send($method, $params=NULL) {
$payload = [
'id' => '1',
'jsonrpc' => '2.0',
'method' => $method,
];
if(!empty($params)) {
$payload['params'] = $params;
}
$timestamp = time();
$signature = static::create_signature($payload, $this->secret, $timestamp);
$headers = [
"content-type: application/json",
"x-api-key: {$this->api_key}",
"x-signature: {$signature}",
"x-timestamp: {$timestamp}",
];
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $this->endpoint,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_SSL_VERIFYHOST => FALSE,
CURLOPT_SSL_VERIFYPEER => FALSE,
]);
$r = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if($httpcode != '200') {
throw new Exception("The status code was not 200.");
}
$res = json_decode($r, TRUE);
if(array_key_exists('error', $res)) {
throw new Exception($res['error']['message']);
}
return $res['result'];
}
}
# configuration
$endpoint = 'http://api.coinrpc.com/bitcoin';
$api_key = 'YOUR_API_KEY';
$api_secret = 'YOUR API SECRET';
# create an instance
$client = ApiClient($endpoint, $api_key, $api_secret);
# make a call to get the wallet balance
try {
$result = $client->send('get_balance', ['currency' => 'BTC']);
print_r($result);
} catch(Exception $e) {
print("error: " . $e->getMessage());
}
Webhook
Each wallet can be configured to support webhook notifications.
See each individual network's webhook documentation for the specific notification events and name/value pairs sent.
Webhook notifcations are sent to your configured URL via POST
method with name/value pairs in standard application/x-www-form-urlencoded
format.
Verification
Every webhook notification contains a verify_hash
name/value pair. This value can be used to validate the authenticity of a webhook notification.
Creating the local verification hash:
- Sort all name/value pairs by name, alphabetically.
- Iterate each sorted pair and add the value to a list. (excluding
verify_hash
name/value pair) - Add the wallet secret as the last item in the list.
- Join the list of values by a semicolon (:).
- Create an SHA256 hash (hex) with the result of the join.
Compare the local hash with the value of the verify_hash
pair.
If they are the same, then the webhook notification is authentic.
Example
The webhook secret is:
WALLET_WEBHOOK_SECRET
Assume we have the following name/value pairs:
guid=ABCD12349876
recipient=1Lbcfr7sAHTD9CgdQo3HTMTkV8LK4ZnX71
amount=0.1
currency=BTC
verify_hash=fbd985e0ddfc6fb63cf8fc3091b06bd992bf64ba9ea6b468f13f27fd372f0913
The alphabetically sorted name/value pair values (excluding verify_hash
) with the webhook secret joined by semicolon would be:
0.1:BTC:ABCD12349876:1Lbcfr7sAHTD9CgdQo3HTMTkV8LK4ZnX71:WALLET_WEBHOOK_SECRET
The SHA256 hex of this string would be:
fbd985e0ddfc6fb63cf8fc3091b06bd992bf64ba9ea6b468f13f27fd372f0913
- Python
- PHP
def create_verify_hash(params, secret):
import hashlib
parts = []
for key in sorted(params.keys()):
if key != 'verify_hash':
parts.append(params[key])
parts.append(secret)
raw = ':'.join(parts)
return hashlib.sha256(raw.encode('utf-8')).hexdigest()
# endpoint handler (assumes flask)
@app.route("/endpoint", methods=["POST"])
def endpoint():
from flask import request
secret = 'WALLET_WEBHOOK_SECRET'
local_hash = create_verify_hash(request.form, secret)
if local_hash != request.form.get('verify_hash'):
raise RuntimeError("verification failed")
# do work with request.form
# ...
function create_verify_hash($params, $secret) {
$parts = [];
ksort($params);
foreach(array_keys($params) as $key) {
if($key != 'verify_hash') {
$parts[] = $params[$key];
}
}
$parts[] = $secret;
$raw = implode(":", $parts);
return hash('sha256', $raw);
}
# endpoint handler
function endpoint() {
$secret = 'WALLET_WEBHOOK_SECRET';
$local_hash = create_verify_hash($_POST, $secret);
if($local_hash != $_POST['verify_hash']) {
throw new Exception("verification failed");
}
# do work with $_POST
# ...
}