Overview
The Cloudcheck API service enables a reporting entity to integrate the Cloudcheck service into its website. This allows the reporting entity to perform electronic verification (EV) on a customer.
Terminology
- reporting entity – you, as the organisation required to perform EV on your customer
- customer – the end user whose details you are required to verify
Server
The API service has a base URL of https://api.cloudcheck.co.nz/ for New Zealand customers, or https://api.cloudcheck.com.au/ for Australian customers.
Request Signing
All requests made to Cloudcheck API endpoints must be signed.
The signature is constructed by taking the request path, appending all request parameters in alphabetical order with a semi-colon after each parameter, and generating an HMAC SHA-256 hash of the result, using the supplied API Secret Key as the HMAC key. This signature must be generated separately for each request made to the API.
To generate the string to use for the signature, sort the parameters alphabetically, and concatenate the request data as follows:
- The path of the request (e.g. /verify/ or /verify/pdf)
- The first request parameter in the format parameter=value (do not URL encode the value when generating the signature)
- A semi-colon
- Repeat steps 2–3 for each remaining parameter
Once this string has been generated, create the HMAC SHA-256 hash from this string and the API Secret Key provided to you.
Finally, convert the bytes returned by the HMAC method to a hexadecimal string, and pass this string to the API as the signature parameter. Note this is not Base64 encoded.
Example
Note this example is for the /verify/
API endpoint. Calls to different endpoints may use different parameters. The signature for all calls must be generated using the parameters included in each specific call.
It is recommended that you use this example data to generate a signature, and ensure it matches the sample result shown before trying to sign test or production data.
The string to calculate the signature from is made up by appending the following items (in order, separated by semi-colons where shown):
Item | Value |
---|---|
The request path | /verify/ |
The data parameter |
data={"details":{},"reference":"1"} |
A semi-colon | ; |
The key parameter |
key=lj5YozlG6cv7mm6c |
A semi-colon | ; |
The nonce parameter |
nonce=1 |
A semi-colon | ; |
The timestamp parameter |
timestamp=1353987581461 |
A semi-colon | ; |
For the above example data, using the API Secret Key Ur7znqOTM1KuQw2gXBt0ShDqQcHVGsAq
, the computed signature is:
a3653be2e753a558fa2ddd9d64be795bf265f23082bbd0d718f4175d16297d10
Each of the parameters, along with the signature
, should be added as GET
or POST
parameters, and submitted to the specified URL for the request being made.
Note this example should only be used to confirm your signature generation is working correctly. The data
parameter above does not include the required data for a working call to the API.
Code Samples
// Access data
String key = "lj5YozlG6cv7mm6c";
String secret = "Ur7znqOTM1KuQw2gXBt0ShDqQcHVGsAq";
String nonce = "1";
String timestamp = "1353987581461";
String data = "{\"details\":{\"address\":{\"suburb\":\"Hillsborough\",\"street\":\"27 Indira Lane\",\"postcode\":\"8022\",\"city\":\"Christchurch\"},\"name\":{\"given\":\"Cooper\",\"middle\":\"John\",\"family\":\"Down\"},\"dateofbirth\":\"1978-01-10\"},\"consent\":\"Yes\"}";
// The API call path.
String path = "/verify/";
// Set up some dummy parameters. Use a TreeMap to sort alphabetically.
Map<String, String> parameterMap = new TreeMap<>();
parameterMap.put("key", key);
parameterMap.put("nonce", nonce);
parameterMap.put("timestamp", timestamp);
parameterMap.put("data", data);
// Build the signature string from the parameters.
StringBuilder signatureString = new StringBuilder(path);
for (String param : parameterMap.keySet()) {
if (param.equals("signature")) {
continue;
}
signatureString.append(param);
signatureString.append("=");
signatureString.append(parameterMap.get(param));
signatureString.append(";");
}
// Create the HMAC SHA-256 Hash from the signature string.
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] generatedHash = sha256_HMAC.doFinal(signatureString.toString().getBytes("UTF-8"));
// Convert the generated hash to a lowercase hexadecimal string.
String signatureHex = DatatypeConverter.printHexBinary(generatedHash).toLowerCase();
// Outputs: 53ccc8563d21393bbac5a5a693e0ba7cc408f83dfb04008122510eee9e80aa86
System.out.println(signatureHex.toString());
// Access data
$key = 'lj5YozlG6cv7mm6c';
$secret = 'Ur7znqOTM1KuQw2gXBt0ShDqQcHVGsAq';
$nonce = '1';
$timestamp = '1353987581461';
$data = '{"details":{"address":{"suburb":"Hillsborough","street":"27 Indira Lane","postcode":"8022","city":"Christchurch"},"name":{"given":"Cooper","middle":"John","family":"Down"},"dateofbirth":"1978-01-10"},"consent":"Yes"}';
// The API call path.
$path = '/verify/';
// Set up some dummy parameters. Sort alphabetically.
$parameterMap = array(
'key' => $key,
'nonce' => $nonce,
'timestamp' => $timestamp,
'data' => $data
);
ksort($parameterMap);
// Build the signature string from the parameters.
$signatureString = $path;
foreach ($parameterMap as $key => $value) {
if ($key === 'signature') {
continue;
}
$signatureString .= "$key=$value;";
}
printf("String: %s\n", $signatureString);
// Create the HMAC SHA-256 Hash from the signature string.
$signatureHex = hash_hmac('sha256', $signatureString, $secret, false);
// Outputs: 53ccc8563d21393bbac5a5a693e0ba7cc408f83dfb04008122510eee9e80aa86
printf("Signature: %s\n", $signatureHex);
// Access data
string key = "lj5YozlG6cv7mm6c";
string secret = "Ur7znqOTM1KuQw2gXBt0ShDqQcHVGsAq";
string nonce = "1";
string timestamp = "1353987581461";
string data = "{\"details\":{\"address\":{\"suburb\":\"Hillsborough\",\"street\":\"27 Indira Lane\",\"postcode\":\"8022\",\"city\":\"Christchurch\"},\"name\":{\"given\":\"Cooper\",\"middle\":\"John\",\"family\":\"Down\"},\"dateofbirth\":\"1978-01-10\"},\"consent\":\"Yes\"}";
// The API call path.
string path = "/verify/";
// Set up some dummy parameters. Use a SortedDictionary to sort alphabetically.
var parameterMap = new SortedDictionary<string, string>();
parameterMap["key"] = key;
parameterMap["nonce"] = nonce;
parameterMap["timestamp"] = timestamp;
parameterMap["data"] = data;
// Build the signature string from the parameters.
var signatureString = new System.Text.StringBuilder();
signatureString.Append(path);
foreach (KeyValuePair<string, string> param in parameterMap) {
signatureString.Append(param.Key);
signatureString.Append("=");
signatureString.Append(param.Value);
signatureString.Append(";");
}
// Create the HMAC SHA-256 Hash from the signature string.
var encoding = new System.Text.UTF8Encoding();
byte[] keyByte = encoding.GetBytes(secret);
byte[] messageBytes = encoding.GetBytes(signatureString.ToString());
using (var hmacsha256 = new HMACSHA256(keyByte)) {
byte[] hashedMessage = hmacsha256.ComputeHash(messageBytes);
string signatureHex = "";
for (int i = 0; i < hashedMessage.Length; i++) {
signatureHex += hashedMessage[i].ToString("X2"); // hex format
}
// Outputs: 53ccc8563d21393bbac5a5a693e0ba7cc408f83dfb04008122510eee9e80aa86
Console.WriteLine("Signature: " + signatureHex.ToLower());
}
# Access data
key = "lj5YozlG6cv7mm6c"
secret = "Ur7znqOTM1KuQw2gXBt0ShDqQcHVGsAq"
nonce = "1"
timestamp = "1353987581461"
data = '{"details":{"address":{"suburb":"Hillsborough","street":"27 Indira Lane","postcode":"8022","city":"Christchurch"},"name":{"given":"Cooper","middle":"John","family":"Down"},"dateofbirth":"1978-01-10"},"consent":"Yes"}'
# The API call path.
path = "/verify/"
# Set up some dummy parameters. Use a TreeMap to sort alphabetically.
parameterMap = {
'key': key,
'nonce': nonce,
'timestamp': timestamp,
'data': data
}
# Build the signature string from the parameters.
signatureString = path
parameterMap.keys.sort.each { |k| signatureString << "#{k}=#{parameterMap[k].to_s};" }
# Create the HMAC SHA-256 Hash from the signature string
signatureHex = OpenSSL::HMAC.digest("sha256", secret, signatureString).unpack("H*")[0].downcase
# Outputs: 53ccc8563d21393bbac5a5a693e0ba7cc408f83dfb04008122510eee9e80aa86
puts signatureHex
import UIKit
import CommonCrypto
// Access data
let key = "lj5YozlG6cv7mm6c"
let secret = "Ur7znqOTM1KuQw2gXBt0ShDqQcHVGsAq"
let nonce = "1"
let timestamp = "1353987581461"
let data = "{\"details\":{\"address\":{\"suburb\":\"Hillsborough\",\"street\":\"27 Indira Lane\",\"postcode\":\"8022\",\"city\":\"Christchurch\"},\"name\":{\"given\":\"Cooper\",\"middle\":\"John\",\"family\":\"Down\"},\"dateofbirth\":\"1978-01-10\"},\"consent\":\"Yes\"}"
// The API call path.
let path = "/verify/"
// Set up some dummy parameters.
let params: [String: Any] = ["key": key, "nonce": nonce, "timestamp": timestamp, "data": data]
// Sort alphabetically and join together.
let paramsString: String = params.keys.sorted().map({"\($0)=\(params[$0]!);"}).joined()
// Build the signature string from the parameters.
let signatureString = path + paramsString
// Create the HMAC SHA-256 Hash from the signature string.
let cKey = secret.cString(using: .utf8)!
let cData = signatureString.cString(using: .utf8)!
var result = [CUnsignedChar](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), cKey, strlen(cKey), cData, strlen(cData), &result)
let hmacData = Data(bytes: result, count: Int(CC_SHA256_DIGEST_LENGTH))
extension Data {
/// Output the data as a lowecase hexadecimal string
var hexEncodedString : String {
return map({String(format: "%02hhx", $0)}).joined()
}
}
// Outputs: 53ccc8563d21393bbac5a5a693e0ba7cc408f83dfb04008122510eee9e80aa86
print(hmacData.hexEncodedString)
Postman Collection
We provide a Postman collection to get you started on your integration with our APIs. Keep in mind the following regarding this collection:
- The examples are provided for illustration purposes only and are not intended to be used in your final implementation. For example, the method used to create a nonce is not industrial-strength and will not reliably produce a unique result.
- The API calls made by this collection use test data sources, therefore you may not get the results that you expect if using real data. In some cases (e.g. Cloudcheck Tree) we don’t have any applicable test data sources, therefore you will get a permissions error.
- The Postman Collection contains a test API key and secret, which will not return any results. If you have been supplied your own API key and secret, please add them to Postman by editing collection, click on the "Variables" tab, and enter your values in the "Current Value" column.
Download the Postman collection.
For instructions on how to install and use this Postman collection, refer to this video.
OpenAPI Documentation
We provide OpenAPI (or Swagger) documentation for our APIs, which can be found here.
Error Codes
Code | Reason |
---|---|
101 | Access key is missing or incorrect. |
102 | One of the required fields listed above is missing or empty. |
103 | The timestamp is older than the maximum allowed. Ensure your timestamp is generated in milliseconds since the Epoch. Note Unix time (which you can see at this site) is in seconds, so you will need to add "000" to the end. |
104 | The supplied nonce parameter has been previously used. |
105 | The supplied verification token is not associated with a verification request for this access key. |
106 | The signature calculated does not match the signature supplied. |
107 | A parameter was supplied in an invalid format. |
108 | You do not have permission to access this resource. |
110 | The query did not return any results. |
111 | The query failed for an unspecified reason. |
112 | The query could not be performed for an unspecified reason. |
113 | The operation could not be performed for an unspecified reason. |
An error response can be at the top level or at the second level, as shown in these examples:
Example
Top level error:{
"error": 102,
"message": "Data parameter not found."
}
Second level error:{
"verification": {
"error": 102,
"message": "Data parameter not found."
}
}
The fields in an error response include:
error
An error code from the table above.message
A description of the error.fields
A comma-separated list of fields in error (optional).errorDetail
A comma-separated list of detailed messages (optional).
Webhooks
When certain events occur we can send a notification to your webhook. A webhook is a URL that is hosted on your site. Please contact if you would like to configure a webhook for your Cloudcheck site.
Cloudcheck expects a 2xx status code, e.g. 200, to be returned by your webhook. If a status other than 2xx is received it will be logged in our sytem.
The events that trigger a call to your webhook are:
- A PEP match review is made in Direct (either for a verification for a ongoing monitoring request).
- A review is made on a Live request in Direct.
- A review is made on an IDscan request in Direct.
- A review is made on an Akahu request in Direct.
- A review is made on a Credfin request in Direct.
- A review is made on a Document Check request in Direct.
We call your webhook using the POST method, passing a json payload that looks like this:
{
"requestType": "<requestType>",
"eventType": "<eventType>",
"reference": "<reference>"
}
where:
<requestType>
is eitherVERIFICATION
,CAPTURE
,IDSCAN
,AKAHU
,CREDFIN
orDOCUMENT_CHECK
.<eventType>
is eitherPEP_MATCH_REVIEW
orREVIEW
.<reference>
is a comma-separated list of references or tokens used to fetch the results using an API call.
For example, the json payload for a PEP match review would look like this:
{
"requestType": "VERIFICATION",
"eventType": "PEP_MATCH_REVIEW",
"reference": "6e1c79ba-c9c0-4f74-b3d4-bdb3e0d85be2"
}
The json payload for a PEP match review for ongoing monitoring would look like this:
{
"requestType": "WATCHLIST_MONITORING",
"eventType": "PEP_MATCH_REVIEW",
"reference": "0c78ee6c-5ef1-4772-a3b8-aab11869dd33,359505a039f0b80d3676ef61ab2dc332dd379a4bd87847055816d743245f61ec"
}
In this example the reference contains both the association reference and the match ID.