Communication Format

Note

This page is only relevant when building new API clients. We provide client libraries for Java, C#, PHP, Python, and Ruby with the below already implemented.

SCORM Cloud’s API is an imperative-style web API. (It is not RESTful.) It has a specific communication format for all API calls.

An API call is a GET or POST to a specific URL. All authenticated web service URLs are of the form:

https://cloud.scorm.com/api
?method=...
&appid=...
&ts=...
&sig=...
...any other parameters...

(without the newlines), where the parameters are:

method

the API method in question; for example, rustici.registration.createRegistration

appid

the application ID in which to operate

ts

the current time – a UTC timestamp of the form yyyyMMddHHmmss. the current time in this format as recorded by SCORM Cloud’s servers can be viewed here

sig

the MD5 “signature” of the API method (defined below)

Unauthenticated API calls are not typically used by customers, but do exist; for example, the above current time link is the rustici.debug.getTime API call:

https://cloud.scorm.com/api?method=rustici.debug.getTime

As above, for regular customers, effectively all API calls are authenticated, and so therefore will need the ts and sig parameters. Since those parameters are ignored on unauthenticated calls – they don’t produce an error – there’s little reason to have special behavior for them in a library.

ts parameter

The POST or GET to the API URL must be made within 15 minutes of the ts parameter’s value. Otherwise, the API will return the error:

Timestamp sent is outside of expiration limit. Likely cause: requesting server clock has drifted. Please sync requesting server time to NTP time.

This error means that the current time (as it is on the SCORM Cloud servers, which are synced to the AWS NTP time servers) is more than 15 minutes away from the ts parameter sent in the URL. There are two primary causes:

  • the API call was made more than 15 minutes after the URL was generated (e.g., embedded in a web-page that a user idled on and then clicked later), or
  • the time on the requesting server is wildly incorrect (which is not often the case)

To re-iterate: API call URLs are only valid for approximately 15 minutes once generated.

sig parameter

The sig parameter is defined as:

MD5( secretKey + parameterString )

where secretKey is one of the secret keys for the application ID included in the parameter list. The parameterString is the UTF8-encoded sorted, concatenated list of key, value pairs for each parameter in the URL. For example, if we wanted to make the API call rustici.registration.exists for the registration with ID 1234 in the application with ID APP123, we’d have the following parameters:

regid: 1234
appid: APP123
method: rustici.registration.exists
ts: 20171024213655

Sort these values case-insensitively:

appid: APP123
method: rustici.registration.exists
regid: 1234
ts: 20171024213655

then concatenate them as key-value pairs with no separator:

appidAPP123methodrustici.registration.existsregid1234ts20171024213655

then prepend the secret key:

someverysecretkeyappidAPP123methodrustici.registration.existsregid1234ts20171024213655

then run that through MD5 to get the sig parameter:

bf38a2e6b2f9a97faf276a7075c9cbc2

which can then be used to build the API call string:

https://cloud.scorm.com/api?method=rustici.registration.exists&appid=APP123&regid=1234&ts=20171024213655&sig=bf38a2e6b2f9a97faf276a7075c9cbc2

The order of the parameters in the URL is irrelevant; the server will perform the same sorting, concatenation, and MD5 process on its side and check if the signature matches the expected value for any of the enabled secret keys for that application. If not, the server will return the error:

The signature attached to the call does not match the signature generated on the server.

This error could mean many different things:

  • the secret key might be incorrect for the app ID in the request
  • not all parameters in the URL were included in the calculation (all parameters in the URL must be)
  • the sorting algorithm did something incorrect (perhaps it was case sensitive, whereas the algorithm is specified as case-insensitive sorting)
  • there were unexpected characters (perhaps a stray newline or space at the beginning or end)
  • it was encoded in something other than UTF-8
  • or any other error that makes the signature mismatch

Note

Here because you received the signature error while using one of our client libraries? That almost certainly means you’ve got the wrong secret key for the app ID you’ve chosen. Contact us at Support for help if things still seem wrong.

Sample Implementation

Here’s a sample implementation of the signing algorithm taken from the Python client library and simplified:

from hashlib import md5

def encode_and_sign(appid, secret, params):
"""
URL encodes the data in the params dictionary, and signs it using the
given secret, if a secret was given.

Arguments:

- appid -- the application ID
- secret -- a secret key for that application
- params -- dictionary containing the key/value parameter pairs
"""

params['appid'] = appid
params['ts'] = datetime.datetime.utcnow().strftime("%Y%m%d%H%M%S")

signing = ''
values = list()

for key in sorted(list(params.keys()), key=str.lower):
    signing += key + params[key]
    values.append(key + '=' + urllib.parse.quote_plus(params[key]))

sig = md5(secret.encode('utf8') + signing.encode('utf8')).hexdigest()
values.append('sig=' + sig)

return '&'.join(values)

The function takes an input like:

encode_and_sign('appid', 'some secret', { 'method': 'rustici.registration.exists', 'regid': '123' })

and returns a string like:

"appid=...&method=rustici.registration.exists&regid=123&ts=...&sig=..."