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>

where:

  • '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:

GET&http://api.elucidat.com/v2/account&oauth_consumer_key=my-consumer-key&oauth_nonce=my-nonce&oauth_timestamp=1434557774

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.

Examples:

PHP

Ruby

Python

Response

 

PHP

/**
<?php
/**
 * 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(
        $auth_headers,
        'Expect:'
    );
    // 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
        );
    }
    else
    {
        $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);
    curl_close($request);
    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);
    unset($auth_headers['oauth_nonce']);

    //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)
{
    ksort($fields);
    //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('https://api.elucidat.com/v2/projects', 'PUBLIC_KEY', 'PRIVATE_KEY');
$headers = auth_headers('PUBLIC_KEY', $nonce);
$fields = array(
    'simulation_mode' => 'simulation'
);
$result = call_elucidat($headers, $fields, 'GET', 'https://api.elucidat.com/v2/projects', 'PRIVATE_KEY');
echo ("HTTP status code: " . $result['status'] . "\n");
print_r($result['response']);

 

Ruby


require 'date'
require 'date'
require 'uri'
require 'net/http'
require 'openssl'
require 'json'
require 'open-uri'
require 'base64'
require 'cgi'
#optional
require 'pp'

class ElucidatClient
def initialize(base_url, public_key, private_key)
@base_url = base_url
@public_key = public_key
@private_key = private_key
end

def call_elucidat(headers, fields, method, url)
# Build a signature
headers['oauth_signature'] = build_signature(headers.merge(fields), method, @base_url + url)

# inititalize http client
method_uri = URI(@base_url + url)
http = Net::HTTP.new(method_uri.host, method_uri.port)
http.use_ssl = true

# create request and package its parameters
if (method == 'GET')
  req = Net::HTTP::Get.new(method_uri.request_uri + '?' + build_base_string(fields, '&'))
else
  req = Net::HTTP::Post.new(method_uri.request_uri)
req.set_form_data(fields)
end

# Add auth headers
req.add_field('Authorization', build_base_string(headers, ','))

# call API
res = http.request req

# raise exception
if there is an error HTTP error
if res.code != '200'
then
raise 'HTTP Error: ' + res.message
end

#
return parsed response data
return JSON.parse(res.body)
end

def get_nonce
headers = auth_headers('')
headers.delete('oauth_nonce')
result = call_elucidat(headers, {}, 'GET', '/v2/account')
result['nonce']
end

def build_signature(fields, request_type, url)
fields = fields.sort
# Build base string to be used as a signature
base_info = request_type + '&' + url + '&' + build_base_string(fields, '&')
# Create the signature from the secret and base string
composite_key = URI::encode(@private_key)
Base64.encode64("#{OpenSSL::HMAC.digest('sha1',composite_key, base_info)}").chop
end

def build_base_string(fields, delim)
base_array = []
fields.each {
  | key, value |
    base_array << CGI.escape(key).gsub(/[+]/, '%20') + '=' + CGI.escape(value).gsub(/[+]/, '%20') # this doesnt work when there are spaces as it rewites them to '+'
}
base_array.join(delim)
end

def auth_headers(nonce) {
  "oauth_consumer_key" => @public_key, "oauth_nonce" => nonce, 'oauth_timestamp' => Time.now.to_i.to_s, 'oauth_version' => '1.0', 'oauth_signature_method' => 'HMAC-SHA1'
}
end
end
client = ElucidatClient.new('https://api.elucidat.com', 'PUBLIC_KEY', 'PRIVATE_KEY')

nonce = client.get_nonce;
headers = client.auth_headers(nonce)
fields = {
  'simulation_mode' => 'simulation'
}
pp client.call_elucidat(headers, fields, 'GET', '/v2/projects')

 

Python 2

import urllib2
import urllib
import json
from hashlib import sha1
import hmac
import time

def call_elucidat(headers, fields, method, url, consumer_secret):
    headers.append(('oauth_signature', build_signature(consumer_secret, headers + fields, method, url)))

    # Build the request string
    fields_string = build_base_string(fields, '&');
    # Prepare the request
    if(method == 'GET'):
        api_query = urllib2.Request( url + '?' +  fields_string )
    else:
        api_query = urllib2.Request( url , fields_string)
    # Set the Authorisation header
    api_query.add_header('Authorization', build_base_string(headers, ','))
    # Make the api call
    api_opener = urllib2.build_opener()
    try:
        response = api_opener.open(api_query)
        # return as json
        return json.loads(response.read())
    except urllib2.HTTPError as e:
        print e.code
        print e.read() 

def get_nonce(api_url, consumer_key, consumer_secret):
    headers = auth_headers(consumer_key)
    for x in xrange(len(headers) - 1, -1, -1):
        if headers[x][0] == 'oauth_nonce':
            del headers[x]
            break
    json = call_elucidat(headers, [('foo', 'bar')], 'GET', api_url + '/v2/account', consumer_secret)
    return json['nonce']

def build_signature(secret, fields, request_type, url):
    sorted_fields = sorted(fields, key=lambda t: t[0])
    # Build base string to be used as a signature
    base_info = request_type + '&' + url + '&' + build_base_string(sorted_fields, '&')
    # Create the signature from the secret and base string
    composite_key = urllib.quote_plus(secret)
    hashed = hmac.new(composite_key, base_info, sha1)
    return hashed.digest().encode("base64").rstrip('\n')

def build_base_string(fields, delim):
    r = []
    for tupple in fields:
        r.append(urllib.quote_plus(str(tupple[0]))+'='+urllib.quote_plus(str(tupple[1])))
    return delim.join(r)

def auth_headers(consumer_key, nonce = ''):
    return [('oauth_consumer_key', consumer_key),
            ('oauth_nonce', nonce),
            ('oauth_signature_method', 'HMAC-SHA1'),
            ('oauth_timestamp', time.time()),
            ('oauth_version', '1.0')]
nonce = get_nonce('https://api.elucidat.com/v2/projects', 'PUBLIC_KEY', 'PRIVATE_KEY')
headers = auth_headers('PUBLIC_KEY', nonce);
fields = [('simulation_mode', 'simulation')]
print call_elucidat(headers, fields, 'GET', 'https://api.elucidat.com/v2/projects', 'PRIVATE_KEY')
                'PUBLIC_KEY'

 

Response

[
  {
    "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": "https://app.elucidat.com/projects/edit/60b64z4491fa5",
    "type": "project"
  }
]

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

Articles in this section

See more
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!