Webhooks

Prev Next

Webhook

Webhooks enable Cirrus to push information to other systems when certain events occur. This contrary to the rest of the REST API where a system will have to call Cirrus' API for information,

Exam results ready

At the moment Cirrus offers one webhook: Exam results ready.
Use this webhook to be notified when new results become available (so your system does not need to fall back on polling which is not permitted under Cirrus' Fair Use policy).

Trigger / Retries

The Exam results ready is triggered when results are published within Cirrus. Since September 2021 if results are updated (e.g. through rescoring) the Exam results ready will be triggered again.

Optionally only when result is satisfactory

Since October 2021 you can configure to only trigger the Exam results ready webhook Only when result is SATISFACTORY, i.e. only when the candidate passed the exam, e.g. useful when the receiving system generates a certificate or badge.

If the receiving server is down, Cirrus will retry for a total of 10 attempts with a randomized exponential delay between each attempt calculated thus:

var random = new Random();
return (int)Math.Round(Math.Pow(attempt - 1, 4) + 15 + random.Next(30) * attempt);
Valid messages should always return 200 (OK)

Your service should always return 200 for valid (including properly authenticated) messages, except if a consequent call to the Cirrus API (i.e. /results) fails. See Response.

If the processing fails on your end, please return 200 (OK), and enqueue the processing on your end. This to prevent unnecessary load and triggering alerts on Cirrus servers.

Create webhook

Create webhook via the cirrus user interface. You must fill following fields
Step 1: Go to Admin > Webservices Group > Webhooks
Step 2: Create webhook

image.png

Field Description
Active An inactive webhook will not trigger request. NOTE that any requests not send will not be send after (re)activation.
Title Title of webhook with a short description
Payload URL URL of the endpoint on your server that will receive the webhook POST requests
Content type Content type that will be used for requests
Authorization type See Authorization Types for Integrity Check below
Secret key For X-Cirrus-Signature, see Authorization Types for Integrity Check below
Username/Password For Basic Authentication, see Authorization Types for Integrity Check below
Token For Bearer token, see Authorization Types for Integrity Check below
Events See Triggers / Retries above.
Payload template See Custom Payload Template below

Response

Webhook response should have http status 200 OK, or any other 2## status, for the successfully received webhook. Other statuses will be considered as an error, and will trigger another attempt.

Valid messages should always return 200 (OK)

Your service should always return 200 for valid (including properly authenticated) messages, except if consequent calls to the Cirrus API fail (1).
E.g. if you receive a result for an unknown schedule as it was manually created in Cirrus do not return an error as it will trigger retries and clog our queues.

(1) if consequent calls to the Cirrus API fail, like /results is the only appropriate scenario to return an error response in order to leverage Cirrus' retry mechanism.

Authorization Types for Integrity Check

If an optional authentication secret is supplied with an webhook endpoint, Cirrus offers several authorization types:

Authorization Type Header Value Remarks
Basic Authentication Authorization login:password or Base64 token If the Secret Key contains a : Cirrus will first Base64 encode⧉ it, else it will be added verbatim
Bearer token Bearer token Cirrus will added the Secret verbatim (if required by your system you need to Base64 encode⧉ it beforehand.)
X-Cirrus-Signature X-Cirrus-Signature SHA-1 hash Validation of message contents. Default for existing customers. See "X-Cirrus-Signature Specification" further down.

Custom Payload Template

Complete JSON template

When providing a custom Payload Template you must provide a complete valid JSON template, including surrounding {}. You can check your template using online JSON validators (e.g. jsonformatter.org

Supported Fields

Field Token Description/Remarks
Timestamp {{timestamp}} ISO timestamp (in UTC) that this result was published
Exam Submission Date {{submissiondate}} ISO timestamp (in UTC) that candidate submitted the exam
Candidate User ID {{candidate_id}}
Assessment External ID {{assessment_id}}
Schedule External ID {{schedule_id}}
Schedule HierarchyExternal ID {{schedule_hierarchy_id}}
Attempt External ID {{attempt_id}}
Is Remarked {{is_remarked}}
Webhook URL {{webhook_url}} The URL you configured to receive the events of this webhook. Added mostly for backward compatibility.
Candidate First or Given name {{candidate_first_name}}
Candidate Last or Family name {{candidate_last_name}}
Candidate Email {{candidate_email}}
Candidate Username {{candidate_username}}
Candidate Language {{candidate_lang_code}} 2-letter ISO code
Event Key {{webhook_event}} Added mostly for backward compatibility.
Event Title {{webhook_title}} Added mostly for backward compatibility.

Default template

{
    "result":  
        {  
            "student_id":"{{candidate_id}}",
            "exam_id":"{{assessment_id}}", 
            "schedule_id":"{{schedule_id}}",  
            "attempt_id":"{{attempt_id}}" 
        },  
    "event":"{{webhook_event}}",  
    "title": "{{webhook_title}}",  
    "created":"{{timestamp}}",   
    "url":"{{webhook_url}}" 
}

Credly

For information only

This information is provided for your information only. Credly's documentation⧉ is authoratitive for the workings of the Credly API and system: https://sandbox.credly.com/docs

Prequisites:

  • A contract with Credly so you have a valid Credly Organisation ID and Credly Authorization Token
  • Authentication Type "Basic Authentication" and "Username" "*credly-auth-token*" i.e. your Credly Authorization Token; e.g. "2yfzJINVALIDxEXAMPLEdRCKI". Leave the "Password" field blank.
  • Credly Template for each Assessment. And the Assessment's External ID is filled with the Credly Template ID.
Create a Credly template per Assessment

As External IDs must be unique so each Assessment requires its own External ID, for this reason you must create a Credly template per Assessment.
An Assessment can of course be (re)used in many times in different Schedules.

Credly URLs

System URL Remarks
Live for normal login Contact Credly
Live Webhook URL Contact Credly
Test for normal login https://sandbox.credly.com Do not use for web hooks.
Test Webhook URL https://sandbox-api.youracclaim.com/v1/organizations/aecb0222-c7f1-4fca-a2fa-37a42e14afcb/badges Replace aecb0222-c7f1-4fca-a2fa-37a42e14afcb with your Credly Organisation ID

Credly Payload template

  • "suppress_badge_notification_email" if true Credly will NOT send a notification to the Candidate's email.
  • "expires_at" null means the Credly certification will not expire.
{
  "recipient_email": "{{candidate_email}}",
  "badge_template_id": "{{assessment_id}}",
  "issued_at": "{{submissiondate}}",
  "issued_to_first_name": "{{candidate_first_name}}",
  "issued_to_last_name": "{{candidate_last_name}}",
  "expires_at": null,
  "issuer_earner_id": "{{candidate_id}}",
  "locale": "en",
  "suppress_badge_notification_email": false
}

Example Request Headers

	"X-Cirrus-Signature": "sha1=ee499df566fcdf8b20c3948d0264f79e4c5644da",
	"Connection": "Keep-Alive",
	"Content-Length": 240,
	"Content-Type": "application/json",
	"Expect": "100-continue", 
	"Host": "http://cirrus.com"  -  Your Host which you provided

Example Response Body

IDs only

It’s by design that the response contains only IDs. They can be used in appropriate additional calls for additional information of interest. E.g. '/get-assessment-results' for the result details.

{
    "result":  
        {  
            "student_id":"student_1",  //userId
            "exam_id":"exam_1",  //Assessment externalId
            "schedule_id":"schedule_1"  //Schedule externalId
            "attempt_id":"attempt_1"  //Attempt externalId
        },  
    "event":"exam-results-ready",  
    "title": "results_hook",  
    "created":"2019-09-12T05:31:01.2833776Z", // UTC Time  
    "url":"http://cirrus.com/results" // Your url which you provided 
}  

X-Cirrus-Signature Specification

This header is of the format:

X-Cirrus-Signature: sha1=f7000d3a2fd51f8f53d91e0085fae1423dfbab8e

Where the above hash is calculated by (OSX and Linux)

echo -n 'request body' | openssl dgst -sha1 -hmac "authentication secret"

This header can be used to detect malicious payloads sent to your endpoint.

How to get data, compute signature and validate is best shown with example code, see the following C# example.

X-Cirrus-Signature C# Example Code

static void Main(string[] args)  
{  
    var server = new HttpListener();  
  
    server.Prefixes.Add("http://<yourdomain.com>/results"); //your url for webhook
    server.Start();  
  
    var context = server.GetContext();  
    var request = context.Request;  
    var response = context.Response;  
  
    //get request body  
    var body = new StreamReader(context.Request.InputStream).ReadToEnd();  
  
    //validate data, optional step, can don't do that  
    var sign = string.Concat("sha1=", SignatureSha1.Sign("your secret key", body));  
    var isValid = request.Headers["X-Cirrus-Signature"] == sign;  
  
    if (!isValid)  
        return; // do something if invalid
  
    response.StatusCode = (int)HttpStatusCode.OK;  
    response.StatusDescription = "OK";  
    response.Close();  
}  
  
public static class SignatureSha1 // class for sign a data  
{  
    public static string Sign(string key, string body)  
    {  
        var byteKey = Encoding.UTF8.GetBytes(key);  
        var byteBody = Encoding.UTF8.GetBytes(body);  
  
        using (var hmac = new HMACSHA1(byteKey))  
        {  
            var result = hmac.ComputeHash(byteBody);  
            var stringResult = result.Aggregate("", (s, e) => s + $"{e:x2}", s => s);  
            return stringResult;  
        }  
    }  
}  

TROUBLESHOOTING

Webhook issues

In order to troubleshoot Webhook issues you need technical expertise and sysadmin access to the detailed logs of the receiving system.

Before you contact support, please:

  1. Carefully check your Webhook configuration in Cirrus, in particular is it "Active", is your "Payload URL" and "Payload template" correct, but also the other fields.
  2. Check in Cirrus if the results for your (test) attempt have been published (or else the Webhook won't have been triggered).
  3. Check the "External ID"s in Cirrus of your Candidate (User ID), Assessment (External ID), and Schedule (External ID)
Information required for support

In case, you must contact our Service Desk for help, please ensure you provide the following:

  • At least one Webhook request as send by Cirrus including URL (your URL), HTTP Headers, and Body;
  • The Cirrus URL, URL of the Cirrus environment, and
  • Exact date and time of Webhook (e.g. when you triggered it by publishing a result in Cirrus).
  • A screenshot of your Webhook Configuration settings screen in Cirrus;
  • The User ID of the candidate and External IDs of both Schedule and Assessment.
  • A screenshot of your published attempt;

Empty Fields

When following steps of Webhook issues, in particular check the "Payload Template" for errors, e.g. tokens misspelled or missing a { or }, and check the related objects in Cirrus if the information is correctly filled in there.
.