Webhooks
  • 29 Nov 2023
  • 7 Minutes to read
  • Contributors
  • Dark
    Light
  • PDF

Webhooks

  • Dark
    Light
  • PDF

Article summary

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

FieldDescription
ActiveAn inactive webhook will not trigger request. NOTE that any requests not send will not be send after (re)activation.
TitleTitle of webhook with a short description
Payload URLURL of the endpoint on your server that will receive the webhook POST requests
Content typeContent type that will be used for requests
Authorization typeSee Authorization Types for Integrity Check below
Secret keyFor X-Cirrus-Signature, see Authorization Types for Integrity Check below
Username/PasswordFor Basic Authentication, see Authorization Types for Integrity Check below
TokenFor Bearer token, see Authorization Types for Integrity Check below
EventsSee Triggers / Retries above.
Payload templateSee 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 TypeHeaderValueRemarks
Basic AuthenticationAuthorizationlogin:password or Base64 tokenIf the Secret Key contains a : Cirrus will first Base64 encode⧉ it, else it will be added verbatim
Bearer tokenBearertokenCirrus will added the Secret verbatim (if required by your system you need to Base64 encode⧉ it beforehand.)
X-Cirrus-SignatureX-Cirrus-SignatureSHA-1 hashValidation 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

FieldTokenDescription/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

SystemURLRemarks
Live for normal loginContact Credly
Live Webhook URLContact Credly
Test for normal loginhttps://sandbox.credly.comDo not use for web hooks.
Test Webhook URLhttps://sandbox-api.youracclaim.com/v1/organizations/aecb0222-c7f1-4fca-a2fa-37a42e14afcb/badgesReplace 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.
.


What's Next