Making your first call to the Project API

All calls to the Project API require authentication. Assuming you've set up an application in your account already, it is time to make your first call to the API.

Initial Steps

There are a few security steps that must be performed for any request to the API:

  • Firstly a cryptographic nonce must be obtained from the API. This is done by making a request to any endpoint without a nonce in the request. The nonce protects your requests from being resent by malicious parties, or in the event of an unexpected technical issue.
  • An Authorization header needs to be created, and it should initially include an 'oauth_consumer_key' (the Public API key presented when you added an application), 'oauth_timestamp' and 'oauth_nonce' (If the nonce is missing, the API will respond with a nonce).
  • To make a request to the API, a signature must be included.
  • The signature validates that the request has not been tampered with. A signature can be created using the following instructions:

1. Create a string representation of the request.

The format of the string is as follows:

<http method>&<url>&<all params>


  • 'http method' can be 'POST' or 'GET',
  • 'url' is the full URL to be called
  • 'all params' is an alphabetically ordered list of the authorization headers and any GET/POST parameters in the format of a query string.

For example:


2. Generate a HMAC  (hash-based message authentication code) of the string representation using a SHA1 hashing algorithm with a url encoded version of your secret key.

3. Base 64 encode the HMAC.

You can then package the signature in the authorization headers as 'oauth_signature'

If you've done all of the above, you should have a crytopgraphic nonce you can use for the call which follows. To make life easier, we've included some examples of this process below.







 * Makes an API request to elucidat
 * @param $headers
 * @param $fields
 * @param $url
 * @param $consumer_secret
 * @return mixed
function call_elucidat($headers, $fields, $method, $url, $consumer_secret)
    //Build a signature
    $headers['oauth_signature'] = build_signature($consumer_secret, array_merge($headers, $fields) , $method, $url);
    //Build OAuth headers
    $auth_headers = 'Authorization:';
    $auth_headers .= build_base_string($headers, ',');
    //Build the request string
    $fields_string = build_base_string($fields, '&');
    //Set the headers
    $header = array(
    // Create curl options
    if (strcasecmp($method, "GET") == 0)
        $url .= '?' . $fields_string;
        $options = array(
            CURLOPT_HTTPHEADER => $header,
            CURLOPT_HEADER => false,
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_SSL_VERIFYPEER => false
        $options = array(
            CURLOPT_HTTPHEADER => $header,
            CURLOPT_HEADER => false,
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_POST => count($fields) ,
            CURLOPT_POSTFIELDS => $fields_string
    //Init the request and set its params
    $request = curl_init();
    curl_setopt_array($request, $options);
    //Make the request
    $response = curl_exec($request);
    $status = curl_getinfo($request, CURLINFO_HTTP_CODE);
    return array(
        'status' => $status,
        'response' => json_decode($response, true)

 * Each request to the elucidat API must be accompanied by a unique key known as a nonce.
 * This key adds an additional level of security to the API.
 * A new key must be requested for each API call.
 * @param $api_url
 * @param $consumer_key
 * @param $consumer_secret
 * @return bool
function get_nonce($api_url, $consumer_key, $consumer_secret)
    // Start with some standard headers, unsetting the oauth_nonce. without the nonce header the API will automatically issue us one.
    $auth_headers = auth_headers($consumer_key);

    //Make a request to elucidat for a nonce...any url is fine providing it doesnt already have a nonce
    $json = call_elucidat($auth_headers, array() , 'GET', $api_url, $consumer_secret);

    if (isset($json['response']['nonce']))
        return $json['response']['nonce'];
    return false;

 * Computes and returns a signature for the request.
 * @param $secret
 * @param $fields
 * @param $request_type
 * @param $url
 * @return string
function build_signature($secret, $fields, $request_type, $url)
    //Build base string to be used as a signature
    $base_info = $request_type . '&' . $url . '&' . build_base_string($fields, '&'); //return complete base string
    //Create the signature from the secret and base string
    $composite_key = rawurlencode($secret);
    return base64_encode(hash_hmac('sha1', $base_info, $composite_key, true));


 * Builds a segment from an array of fields. Its used to create string representations of headers and URIs
 * @param $fields
 * @param $delim
 * @return string
function build_base_string($fields, $delim)
    $r = array();
    foreach ($fields as $key => $value)
        $r[] = rawurlencode($key) . "=" . rawurlencode($value);
    return implode($delim, $r); //return complete base string

 * Returns typical headers needed for a request
 * @param $consumer_key
 * @param $nonce
function auth_headers($consumer_key, $nonce = '')
    return array(
        'oauth_consumer_key' => $consumer_key,
        'oauth_nonce' => $nonce,
        'oauth_signature_method' => 'HMAC-SHA1',
        'oauth_timestamp' => time() ,
        'oauth_version' => '1.0'
$nonce = get_nonce('', 'PUBLIC_KEY', 'PRIVATE_KEY');
$headers = auth_headers('PUBLIC_KEY', $nonce);
$fields = array(
    'simulation_mode' => 'simulation'
$result = call_elucidat($headers, $fields, 'GET', '', 'PRIVATE_KEY');
echo ("HTTP status code: " . $result['status'] . "\n");


Python 3.9

(Please note, you may need to install the Requests module manually to use this code. Keys should be inserted where indicated at the bottom of the code block.)

import base64
import hmac
import json
import requests
import time
import urllib
from collections import OrderedDict
from hashlib import sha1
from urllib import parse
from pprint import pprint

class Api:
     Core API functions

    def __init__(self, api_root, endpoint, method, params, data, private_key, public_key):
        self.api_root = api_root
        self.endpoint = endpoint
        self.method = method
        self.private_key = private_key
        self.public_key = public_key
        self.headers = {} = data
        self.params = params
        self.nonce = ''
        self.response = None

    def send_request(self):
        url = self.api_root + self.endpoint
        self.response = requests.request(self.method, url,, headers=self.headers, params=self.params)

        return self.response

    def build_signature(private_key: str, auth_header: dict, params: dict, data: dict, request_type: str, url: str):
        ordered_header = dict(OrderedDict(sorted(auth_header.items())))
        ordered_header = urllib.parse.urlencode(ordered_header, quote_via=urllib.parse.quote)
        base_string = str(ordered_header).replace(": ", "=").replace(", ", "&").replace("'", "").strip("{}")
        base_info = (request_type + "&" + url + "&" + base_string).encode('utf8')
        encoded_key = (urllib.parse.quote_plus(private_key)).encode('utf8')

        hashed =, base_info, sha1).digest()
        signature = urllib.parse.quote_plus(base64.b64encode(hashed).decode())

        return signature

    def _get_nonce(self):
        timestamp = str(int(time.time()))
        auth_header = {'oauth_consumer_key': self.public_key,
                       'oauth_timestamp': timestamp,
                       'oauth_signature_method': 'HMAC-SHA1',
                       'oauth_version': '1.0'}
        url = self.api_root + self.endpoint
        oauth_signature = self.build_signature(self.private_key, auth_header, self.params,, self.method, url)
        self.headers = {'Authorization': f'oauth_consumer_key={self.public_key},oauth_signature_method=HMAC-SHA1,'
        body = json.loads(self.response.text)
        nonce = body.get('nonce')
        return nonce

    def call_elucidat(self):
        timestamp = str(int(time.time()))
        self.nonce = self._get_nonce()
        auth_header = {'oauth_consumer_key': self.public_key,
                       'oauth_signature_method': 'HMAC-SHA1',
                       'oauth_nonce': self.nonce,
                       'oauth_timestamp': timestamp,
                       'oauth_version': '1.0'}
        url = self.api_root + self.endpoint
        oauth_signature = self.build_signature(self.private_key, auth_header, self.params,, self.method, url)
        self.headers = {
            'Authorization': f'oauth_consumer_key={self.public_key},oauth_nonce={self.nonce},'
        response = self.send_request()
        pprint("Response code: " + str(response.status_code))
        pprint("Response text: " + response.text)

if __name__ == '__main__':
     private_key = '[PRIVATE_KEY]'
     public_key = '[PUBLIC_KEY]'
     api_root = ''



    "project_code": "60b64z4491fa5",
    "name": "Audio Test",
    "created": "2021-09-01 15:07:48",
    "project_views": "0",
    "project_views_this_month": "0",
    "project_passes": "0",
    "project_fails": "0",
    "theme_skin_id": "97898",
    "name_in_release": "",
    "language": "en-GB",
    "theme": "master_theme",
    "theme_version_number": "1",
    "scorm_mode": "1.2",
    "lrs_endpoint": null,
    "lrs_endpoint_username": null,
    "learner_access": "any",
    "project_description": null,
    "use_learner_service": "0",
    "edit_url": "",
    "type": "project"

Was this article helpful?
0 out of 0 found this helpful

Articles in this section

Request support
Access support that’s tailored to you by getting in touch with our Support Team.
Send us feedback
Do you have a new feature request, or want to tell us about something that works well (or not so well) for you? Get in touch!