Step-by-step instructions for signing Confident Cannabis API requests with HMAC-SHA256.
By default, all requests to the Confident API have to be signed using a valid API key and API secret. This is an extra step when integrating (and can be a little intimidating) but it is important so that we can be sure your requests are really coming from you, which helps us protect your account and your information.
This page explains all the steps you need to correctly sign API requests. If you ever get stuck, check out the example code and our support channels. All the code on this page has been combined for easier access (with the crypto library imported) and is available as part of the examples here.
Disabling Signing
To speed development you can disable signing on a per-credential basis. This makes all requests require only the X-ConfidentCannabis-APIKey header. You can enable/disable signing from the organization settings page — you may want to do this while in development to get started more quickly.
How Does Request Signing Work?
Request signing uses a pair of known credentials (your API key and API secret) to first identify your account (the API key part) and second to create a cryptographic "signature" of the important data being sent. The server also knows the API secret and can calculate the same signature using the data it actually received.
Different data or an incorrect API secret (or a long delay) results in a different signature, effectively proving to the server that the request really did come from you (or at least someone with both the API key and secret).
Generating a Signature — Step by Step
The examples below are written in JavaScript (the same code the legacy interactive docs used), but the algorithm translates to any language.
All the code in this example signs a request with the following parameters:
var method = 'GET';
var route = '/api/v0/signingtest/';
var headers = {'X-ConfidentCannabis-Timestamp': '1474507118.77095'};
var data = {'foo': 1, 'bar': 2};
var apiKey = '88b750a8-d414-4aee-b26c-2cc7e85434dd';
var apiSecret = '043bca27-c4d1-4d39-86d6-e5f0c3b4bb4f';Signing is broken into three phases — creating one big string from all the important parts of the request, cryptographically hashing that string using your API secret, and combining the results with some extra bits so the signature can be validated.
At the end of these instructions we will have used the above info to create this final signature:
CC0-HMAC-SHA256:x-confidentcannabis-timestamp:1fdc8a407c5d1c31df2334fbc49984062a4071077a9dc7cfff4de934902c01b8
Prerequisite — find a reliable crypto library for your language
The Confident API uses HMAC-SHA256 signatures, so make sure you have a library that can correctly generate them. These docs use the CryptoJS library for JavaScript.
To ensure your hashing library works correctly, compare these sample inputs and outputs to your own results:
CryptoJS.HmacSHA256('foo', 'bar').valueOf()
// 147933218aaabc0b8b10a2b3a5c34684c8d94341bcf10a4736dc7270f7741851
CryptoJS.HmacSHA256('test', 'secret').valueOf()
// 0329a06b62cd16b33eb6792be8c60b158d89a2ee3a876fce9a881ebb488c0914
CryptoJS.HmacSHA256('longer_content', 'longer_key').valueOf()
// 3b3e8443e5156d9422998442c014a0ee2a8c398f29e13442dbf3301401bb192aPrerequisite — test your language/library encoding
Inevitably, each language and encoding library handles percent encoding differently. The Confident API uses a combination of Python's stdlib urllib.quote_plus and urllib.urlencode behavior, which results in nearly every character being percent-encoded — except, notably, space, which becomes a + sign.
Before writing your signing code, triple check your percent-encoding tools to ensure everything is handled correctly (including punctuation and unicode). For example, here is the code used to correctly percent encode each field in JavaScript:
function specialEncodeComponent(component) {
// special cases! encodeURIComponent does NOT escape everything
// required to match how the expected encoding works
var specialCases = [
[/!/g, '%21'],
[/'/g, '%27'],
[/\(/g, '%28'],
[/\)/g, '%29'],
[/\*/g, '%2A'],
[/~/g, '%7E'],
[/%20/g, '+'] // turn spaces back from %20 into +
];
var component = encodeURIComponent(component);
for (var i = 0, n = specialCases.length; i < n; i++) {
component = component.replace(
specialCases[i][0], specialCases[i][1]);
}
return component;
}Please refer to the example libraries and encoded-character list for details.
1. Create the base string by concatenating the request method and route
Concatenate the HTTP method (here, the string GET) and the route to get the first part of the signing string.
var baseString = method.toUpperCase() + route;
console.log('baseString:', baseString);
// baseString: GET/api/v0/signingtest/2. Create a sorted, lowercased list of (key, value) pairs from headers
Create a list of all the headers that will be signed (you do not have to sign every header, but you must at least include an X-ConfidentCannabis-Timestamp header with the current epoch timestamp in seconds). Make sure everything is lowercased. This list is used in two later steps.
var headers = {'X-ConfidentCannabis-Timestamp': '1474507118.77095'};
var sortedHeaderKeys = Object.keys(headers).sort();
var sortedHeaders = sortedHeaderKeys.map(function(headerKey) {
return [headerKey.toLowerCase(), ('' + headers[headerKey]).toLowerCase()];
}).sort();
console.log('sortedHeaders:', sortedHeaders);
// sortedHeaders: [ [ 'x-confidentcannabis-timestamp', '1474507118.77095' ] ]3. Create a url-encoded param string for the ordered header fields
Take the sorted, lowercased headers and create a string combining each key and value with an equals sign, with ampersands between pairs: key=value&....
Important: to correctly encode the param string you must handle character encoding the way the API expects. Every language and library handles this differently — check your code against the example project tests and the encoding chart.
var headerString = sortedHeaders.reduce(function(prev, curr) {
var component = encodeURIComponent(curr[0]) + '=' + encodeURIComponent(curr[1]);
prev.push(component);
return prev;
}, []).join('&');
console.log('headerString:', headerString);
// headerString: x-confidentcannabis-timestamp=1474507118.770954. Create a semicolon-separated list of lowercase header keys
Create another string — just the lowercase header keys (no values), separated by semicolons. The server uses this to know which headers are included in the signature.
var headerListString = sortedHeaderKeys.map(function(headerKey) {
return headerKey.toLowerCase();
}).join(';');
console.log('headerListString:', headerListString);
// headerListString: x-confidentcannabis-timestamp5. Create a sorted list of (key, value) pairs from the form data
Create a list of (key, value) pairs, sorted alphabetically ascending, from each of the form fields.
var sortedKeys = Object.keys(data).sort();
var sortedParamsList = sortedKeys.map(function(key) {
return [key, data[key]];
});
console.log('sortedParamsList:', sortedParamsList);
// sortedParamsList: [ [ 'bar', 2 ], [ 'foo', 1 ] ]6. Add ('api_key', yourApiKey) to the END of the list
sortedParamsList.push(['api_key', apiKey]);
console.log('sortedParamsList:', sortedParamsList);
// sortedParamsList: [
// [ 'bar', 2 ],
// [ 'foo', 1 ],
// [ 'api_key', '88b750a8-d414-4aee-b26c-2cc7e85434dd' ]
// ]7. Create the url-encoded param string
Create a urlencoded string from the ordered parameters list by percent-encoding each key and value, combining them with = between key and value and & between pairs (using the specialEncodeComponent helper from the prerequisites):
var paramString = sortedParamsList.reduce(function(prev, curr) {
var component = specialEncodeComponent(curr[0]) + '=' + specialEncodeComponent(curr[1]);
prev.push(component);
return prev;
}, []).join('&');
console.log('paramString:', paramString);
// paramString: bar=2&foo=1&api_key=88b750a8-d414-4aee-b26c-2cc7e85434dd8. Percent-encode the base string from step 1
var encodedBaseString = encodeURIComponent(baseString);
console.log('encodedBaseString:', encodedBaseString);
// encodedBaseString: GET%2Fapi%2Fv0%2Fsigningtest%2F9. Combine the encoded base string, header string, and parameter string
Take the results from steps 8, 3, and 7 and combine them with an ampersand between them. This is the canonical message string used to create the actual signature.
var signingString = [
encodedBaseString,
headerString,
paramString
].join('&');
console.log('signingString: ' + signingString);
// signingString: GET%2Fapi%2Fv0%2Fsigningtest%2F&x-confidentcannabis-timestamp=1474507118.77095&bar=2&foo=1&api_key=88b750a8-d414-4aee-b26c-2cc7e85434dd10. Create the SHA256 HMAC signature using your API secret
Run the signing string through your crypto library's HMAC-SHA256 function with your API secret as the key. This produces the hexadecimal part of the full signature.
var rawSignature = '' + CryptoJS.HmacSHA256(signingString, apiSecret).valueOf();
console.log('raw signature: ', rawSignature);
// raw signature: 1fdc8a407c5d1c31df2334fbc49984062a4071077a9dc7cfff4de934902c01b811. Construct the final signature
Create the final signature by adding the algorithm string (CC0-HMAC-SHA256) and the header list string to the front of the raw signature, with colons between each piece.
var signature = 'CC0-HMAC-SHA256:' + headerListString + ':' + rawSignature;
console.log('final signature:', signature);
// final signature: CC0-HMAC-SHA256:x-confidentcannabis-timestamp:1fdc8a407c5d1c31df2334fbc49984062a4071077a9dc7cfff4de934902c01b812. Add the signature header to the request
At this point you have successfully generated your request signature! Be sure to include the X-ConfidentCannabis-Timestamp, X-ConfidentCannabis-APIKey, and X-ConfidentCannabis-Signature headers in your request.
Signed requests must reach the server within 30 seconds of the timestamp or they are rejected with request_too_old (the response includes current_server_time for clock calibration).
You can verify your implementation end-to-end against the POST /v0/signingtest endpoint.