General Information

These are the official docs for the v0 API. Version 0 is currently under active development and is not yet considered stable — as such, there may be breaking changes introduced without incrementing the version number.


API Endpoints

All endpoints use the standard HTTP verbs to describe their behavior (GET, POST, PUT, DELETE) and use status codes to categorize their responses (200, 301/302, 400, 401, 404, 405, 500). In general, a correctly signed and formatted request will return with status code 200.

When sending data, use a correctly escaped querystring for GET endpoints or a standard form upload (application/x-www-form-urlencoded) for POST/PUT/DELETE. If you are using a request library, this is likely handled for you automatically.

📘

Request bodies are form-encoded, not JSON

The API does not accept application/json request bodies. Endpoints that take complex objects (like creating an order) accept a JSON-encoded string as a single form field — each endpoint's reference documents this.

Every endpoint returns a JSON formatted response. After receiving a 200, you should always check the success field to see if your request completed as expected.

GET requests generally always succeed, but any POST/PUT/DELETE request can fail because of pre-requisites or invalid state (like a name conflict or requiring something to exist), which will set the success flag to false. When success is false, there will be an error_code field (and potentially error_message and error_details) to help you identify and react to whatever caused the problem.

Response Envelope

Every response body has roughly the same pieces:

  • success — true/false flag that is always present
  • error_code — a string code if an error occurred (always present when
    success is false)
  • error_message — a human readable error message
  • error_details — per-field validation errors when error_code is
    invalid_request, keyed by field name

Common error codes returned by every endpoint:

CodeStatusMeaning
missing_api_key401Missing X-ConfidentCannabis-APIKey header
invalid_api_key401API key is not valid (deleted, not found, etc.)
api_access_denied401Account does not have access to the API
missing_signature401Missing X-ConfidentCannabis-Signature header
missing_timestamp401Missing X-ConfidentCannabis-Timestamp header
invalid_timestamp401Timestamp header is not a valid epoch timestamp
request_too_old400Signed request timestamp is too old — includes current_server_time in the response for calibration
invalid_signature401Request is not correctly signed
permission_denied403Account does not have permission for this action
invalid_request400Generic problem with the request (usually validation)

Authentication and Signing

All endpoints (even GETs) require an API key header (X-ConfidentCannabis-APIKey). By default, requests must also be signed with a signature generated using the matching API secret — incorrectly signed requests are rejected with a 401 response. Signing can be disabled per credential from the organization settings page to speed up development.

The signature is the hex-encoded SHA256-HMAC of the API key, the data fields (sorted alphabetically, ascending), and the API secret. Never send your API secret in a request — it should only be used for generating signatures.

Read the full, step-by-step signing instructions on the Request Signing page and see example code that correctly signs requests at github.com/ConfidentCannabis/public-api-tools.

Summary of steps to generate a signature:

  1. Create the base string by combining method and route — e.g. GET/v0/test
  2. Create an ascii-sorted (ascending), lowercased list of (key, value) pairs from the headers dictionary (must include X-ConfidentCannabis-Timestamp but not X-ConfidentCannabis-APIKey or X-ConfidentCannabis-Signature)
  3. Create a url-encoded string key=value&... for the ascii-ordered header fields, lowercased
  4. Create a semicolon-separated list of lowercase header keys — e.g. x-confidentcannabis-timestamp;host
  5. Create an ascii-sorted list of (key, value) pairs from the form data
  6. Add ('api_key', <your api key>) to the END of the list
  7. Create a url-encoded param string key=value&... for the ordered data fields
  8. Percent-encode the base string from step 1
  9. Combine the percent-encoded base string, url-encoded header string, and url-encoded parameter string with & between them
  10. Create the SHA256 HMAC signature of that string using your API secret
  11. Prefix with the signing algorithm and header list string:
    CC0-HMAC-SHA256:host;x-confidentcannabis-timestamp:<signature>

Additional Notes

Updates

All updates and notifications regarding the API are sent via an email list. Please email [email protected] to automatically subscribe. This includes notifications of upcoming features, breaking changes, and version upgrades.

Signing

Getting signing right is the hardest (but most important) part of interacting with the API — the POST /v0/signingtest endpoint exists explicitly for testing your signing code during development.

Paging

Many endpoints that return dynamic lists (e.g. clients, orders, and samples — but not test types, sample categories, etc.) are limited to 100 results and include a more_results boolean field. If more_results is true, additional results exist, which can be queried by passing a number for start (default 0)
and optionally limit (default 100), allowing you to iterate through pages until more_results is false.

Timestamps

Timestamps are always returned in isoformat — for example:
2016-10-04T13:20:42.395276.

Dates with times should always be sent in UTC time in the format
YYYY-MM-DD HH:MM:SS — for example: 2016-10-04 13:20:42.

Dates should always be sent as YYYY-MM-DD — for example: 2016-10-04.

Currency and Pricing

All currency and pricing numbers are always represented as integer cents (instead of floating point dollars). Currency and pricing fields are generally named to reflect this.

File Uploads

Several endpoints support uploading files (images, PDFs, etc.). These file fields are not included when generating signatures, so be sure to exclude them during the signing process. Send files as standard multipart/form-data uploads.

Additional Help

If you need any help or just have questions, comments, or complaints, please reach out at [email protected]. We want to make sure you have the best experience possible, so definitely let us know what you think or if you get stuck!