Skip to content
This repository was archived by the owner on Sep 2, 2020. It is now read-only.

Application Review Webhooks

SueSmith edited this page Sep 28, 2014 · 13 revisions

This guide will demonstrate how you can configure a webhook to receive notification of badge application reviews being submitted via BadgeKit API. If you haven't seen it already, you may want to check out the guide to Submitting Applications first. We will add our webhook URL to the BadgeKit database so that it receives notification of badging events. After that we will process the data BadgeKit API sends when an application review is submitted, building the data into an email to send to the badge earner.

For an explanation of how assessment works in BadgeKit, see Assessment with BadgeKit.

The API guides use node.js examples to demonstrate the key processes you need to connect BadgeKit to your own earner-facing site. If you're using other languages/ platforms, you should be able to see how you can use BadgeKit to deliver your implementation.

Contents

Overview

Below is a detailed guide to using the API in the context of a node.js app - here's the short version!

See the API docs for more detail on the webhook.

Config

To receive data at a webhook URL, you first need to add it to your BadgeKit API database, in the webhooks table. You need an entry including your webhook URL and a secret you will use to decode the data sent there from the API.

Authentication

You can authenticate data received from the API at your webhook URL as follows:

  • Verify the JWT token sent in the Authorization header - use the secret you included in the API webhooks table.
  • Check the hash sent in the Authorization header token against a hash of the received body data.

See the Authorization document for more.

Example Data Received

Data sent to the webhook on application review includes the action, application, review and approved objects:

{
    "action": "review",
    "application": 
    {
        "id": 1,
        "slug": "abcdefg1234567",
        "learner": "earner@adomain.com",
        "created": "2014-05-12T15:13:07.000Z",
        "assignedTo": null,
        "assignedExpiration": null,
        "badge": 
        {
            "id": 1,
            "slug": "great-badge",
            "name": "Great Badge",
            "strapline": "Does great stuff.",
            "earnerDescription": "This badge shows how great you are.",
            "consumerDescription": "The earner of this badge is great.",
            "issuerUrl": "issuersite.com",
            "rubricUrl": "",
            "timeValue": 0,
            "timeUnits": "minutes",
            "limit": 0,
            "unique": 0,
            "created": "2014-05-05T14:17:46.000Z",
            "imageUrl": "http://issuersite.com/images/badge/1",
            "type": "",
            "archived": false,
            "system": 
            {
                "id": 1,
                "slug": "badgekit",
                "url": "http://systemsite.com",
                "name": "Excellent System",
                "email": null,
                "imageUrl": null
            },

            "criteriaUrl": "http://issuersite.com/system/badgekit/badge/great-badge/criteria",
            "criteria": 
            [
                {
                    "id": 1,
                    "description": "Does great things often.",
                    "required": 1,
                    "note": ""
                },

                {
                    "id": 2,
                    "description": "Is generally great.",
                    "required": 1,
                    "note": ""
                }
            ],

            "categories": 
            [

            ]
        },

        "processed": null,
        "evidence": 
        [

        ]
    },

    "review": 
    {
        "id": 1,
        "slug": "1234567abcdefg",
        "author": "reviewer@adomain.com",
        "comment": "Fantastic job on your application.",
        "reviewItems": 
        [
            {
                "criterionId": 1,
                "satisfied": 1,
                "comment": "Exactly right."
            },

            {
                "criterionId": 2,
                "satisfied": 1,
                "comment": "Perfect."
            }
        ]
    },

    "approved": true
}

For a more detailed overview of handling the application review webhook, read on.

Application Reviews

You can allow people to earn the badges you publish via BadgeKit in various ways, one of which involves applications. With this approach, as an issuer, you can list available badges on your site, allowing potential earners to submit applications, supplying supporting evidence. If you're using the BadgeKit Web app, submitted applications will appear in the Applications > Pending section.

When a reviewer assesses a pending application and submits their review, your site can receive notification of this event. This allows you to communicate with the earner, informing them whether or not their application was successful and optionally forwarding feedback from the reviewer. You can configure how you deliver the information (i.e. via your site or via email) to suit your own community.

Note that when an application is assessed as meeting the criteria for a badge, in which case the application status is approved, this does not mean that the badge is automatically issued to the earner. Instead, your site must create a badge instance following a successful review - optionally after offering the badge to the earner and awaiting their confirmation. This allows you to manage cases where permissions may be required for younger earners. When you do use the API endpoints to create the badge instance, you can receive another notification at your webhook URL. At this point you can follow up with the earner, for example offering to push their new badge to a backpack.

The following represents a typical flow for this process:

  • Earner applies for a badge.
  • Reviewer assesses badge application.
  • Webhook receives application review submitted notification.*
  • Webhook communicates with earner, informing them of the review.*
  • If the application was approved, the earner is offered the badge.*
  • If the earner chooses to accept the badge, issuer site creates a badge instance.
  • Webhook receives badge issued notification.
  • Issuer site follows up with earner, e.g. offering to push badge to backpack.

*In this guide we will demonstrate the following parts of this process: configuring a webhook in the API database; receiving the application review notification in the webhook; processing the application review data and including it in an email to the earner.

Even if you do not plan on using the process as it is outlined above, the code examples below will demonstrate how to receive and process the application review webhook data. You can see a more complete working example of this process in the Open Badges - Badges code.

Configuring a Webhook

Your BadgeKit API instance database contains a webhooks table. To receive notification of badging events, insert a record in the table, including:

  • the URL at which you are going to process the received data
  • a secret you will use to decode the data
  • the ID for the system your webhook is going to be associated with.

The following sample SQL demonstrates:

INSERT INTO 'webhooks' ('id', 'url', 'secret', 'systemId') VALUES (null, 'http://issuersite.com/hook', 'asecret', 1);

Verifying the Data

At the URL you specified in your webhook table, you can process the data sent from the API when an application is reviewed. To demonstrate the process in a node.js app, we will use the following resources:

var jws = require('jws');
var bodyParser = require('body-parser');
app.use(bodyParser.json());
var crypto = require("crypto");

You will need these installed (locally or included in a package.json file as dependencies if you're pushing to a service such as Heroku). The following function will receive the webhook data (with a URL ending /hook listed in the database table example above):

app.post('/hook', function(req, res) {
//process notifications
});

The code inside the function will execute when the webhook data is received from the API. We will authenticate the data before we process it, so we begin inside the function by retrieving the authorization header and verifying it:

var token = req.headers.authorization;
token = token.slice(token.indexOf('"')+1, -1);
if (!jws.verify(token, 'asecret')) { //use your secret
	console.log("verification failed");
}
else{
	//process the data
}

The API sends an authorization token in the header, so we check it using the secret we set in the webhook database table. The remainder of the code will be placed in the else block, so that we don't process the data unless verification is successful. See the Authorization doc for more details.

Inside the else block, we can carry out another verification check, this time on the body data received:

var decodedToken;
try {
	decodedToken = jws.decode(token); 
	if (decodedToken.payload.body.hash !== crypto.createHash('sha256').update(JSON.stringify(req.body)).digest('hex')) {
		console.log("body hash does not match token hash");
	}
	else {
		//process review data
	}
} catch(err) {
	console.log("error decoding the data");
}

Here we check the hash of the received body against the hash sent along with it in the authorization header token - this way we know the data received is what the API sent. We will only proceed to communicate with the earner if this verification is also successful, so the rest of the code will be placed in this new else block.

Processing the Data

Now that we've verified the data, we can go ahead and find out what's in there. The API will send data to the webhook URL when various actions occur, but in this case we're only interested in the review event - this occurs when a review is submitted to the API (either via the BadgeKit Web app or another method). We can find out what action has caused the webhook to execute by checking the action field in the data.

We will use a switch statement in the code example as you may later add support for the other webhook notifications. We are going to build a text string to include as the body of an email we will send to the earner, so we start it as an empty string. The data sent to the hook from the API includes many items you may wish to retrieve - the following demonstrates a relatively simple case.

var action = req.body.action;
var info="";
var emailTo="";
switch(action) {
	//review event
	case 'review':
		//earner email
		emailTo=req.body.application.learner;
		//build badge name into email
		info+="<p>Your application for the following badge was reviewed:"+ 
			"<strong>"+req.body.application.badge.name+"</strong></p>";

		//respond differently if approved or not
		if(req.body.approved){ 
			info+="<p>Great news - your application was approved!</p>";
			//include link to accept the badge
			// - alter for your url
			info+="<p><a href="+
				"'http://issuersite.com/accept?badge="+
				req.body.application.badge.slug+
				"&earner="+req.body.application.learner+
				"&application="+req.body.application.slug+
				"'>Accept your badge</a></p>";
		}
		else{
			info+="<p>Unfortunately your application was unsuccessful this time. "+
				"You can re-apply for the badge any time though!</p>";
		}
		//review includes multiple feedback and comment items
		info+="<p>The reviewer included feedback for you:</p>";
		info+="<ul>";
		//comment field includes everything in the Finish page in BadgeKit Web app
		info+="<li><em>"+req.body.review.comment+"</em></li>";
		//review items array, one per criteria - build into list
		var reviewItems = req.body.review.reviewItems;
		var r;
		for(r=0; r<reviewItems.length; r++){
			info+="<li><em>"+reviewItems[r].comment+"</em></li>";
			//can also include whether each criteria item was satisfied
		}
		info+="</ul>";
		info+="<p><strong><em>Thanks for applying!</em></strong></p>";
		break;
}

We use a few of the data items sent to the webhook, which includes the action, application, review and approved fields. The application and review objects provide you with various other pieces of information related to the badge application.

The review sent to the webhook includes multiple data items - the following is a slightly simplified example of the data the webhook URL may receive for an application review:

{
	"action": "review",
	"application": 
	{
		"id": 123,
		"slug": "abcdefg1234567",
		"learner": "earner@adomain.com",
		"created": "2014-05-12T15:13:07.000Z",
		"assignedTo": null,
		"assignedExpiration": null,
		"badge": 
		{
			"id": 61,
			"slug": "mozilla-webmaker-basic-digital-mentor",
			"name": "Mozilla Webmaker Basic Digital Mentor",
			"strapline": "A commitment to exploring, encouraging and shepherding the development of digital expertise in the Mozilla Webmaker community and beyond.",
			"earnerDescription": "This badge acknowledges your commitment to exploring, encouraging, and shepherding community members inthe development of digital expertise in the Webmaker community. You earn this badge by working with the Mozilla Webmaker Team during 2014.",
			"consumerDescription": "Webmaking is an essential component to the sustainability of the worldwide web. This badge represents a personal commitment to exploring, encouraging, and shepherding community members in the development of digital expertise in the Mozilla Webmaker community. It involves any of the following activities: creating a teaching resource; hosting a webmaker event; or making and sharing through the Teach the Web online community. This badge is issued by the Mozilla Webmaker Team during 2014.",
			"issuerUrl": "www.webmaker.org",
			"rubricUrl": "",
			"timeValue": 0,
			"timeUnits": "minutes",
			"limit": 0,
			"unique": 0,
			"created": "2014-05-05T14:17:46.000Z",
			"imageUrl": "http://issuersite.com/images/badge/123",
			"type": "",
			"archived": false,
			"system": 
			{
				"id": 1,
				"slug": "badgekit",
				"url": "http://systemsite.com",
				"name": "Excellent System",
				"email": null,
				"imageUrl": null
			},

			"criteriaUrl": "http://issuersite.com/system/badgekit/badge/mozilla-webmaker-basic-digital-mentor/criteria",
			"criteria": 
			[
				{
					"id": 221,
					"description": "This badge demonstrates the earner has met Mozilla's stringent requirements for Basic Digital Mentor. This badge represents commitment to exploring, encouraging, and shepherding community members in developing digital expertise. It acknowledges the combined community building aspects of teaching and learning. Webmaker Mentors are an essential aspect to Mozilla's mission of making the web.",
					"required": 1,
					"note": ""
				},

				{
					"id": 231,
					"description": "The Basic Digital Mentor has taken one of our training courses and effectively completed one of the following requirements. Create a teaching resource. Create a hackable teaching kit using one of our handy templates. Submit your kit, activity, teaching guide or other teaching resource to help others teach the web or digital literacy to webmaker.org. Host a Webmaker event. Organize a hack jam, learning party or classroom event. Then document and share outcomes with the community. Our handy event guides make it easy. Make and share through the Teach the Web online community. Connect and learn other mentors around the world through remixing, making and discussion.",
					"required": 1,
					"note": ""
				},

				{
					"id": 241,
					"description": "This badge is issued by the Mozilla Webmaker Team during 2014. Mozilla Webmaker helps people teach digital and web literacy, learn new coding skills and make amazing things using digital tools.",
					"required": 1,
					"note": ""
				}
			],

			"categories": 
			[
				
			]
		},

		"processed": null,
		"evidence": 
		[
			
		]
	},

	"review": 
	{
		"id": 456,
		"slug": "1234567abcdefg",
		"author": "reviewer@adomain.com",
		"comment": "Fantastic job on your application.\n\nBest. Application. Ever.\n\nEverything incredible.\n\n\n\n",
		"reviewItems": 
		[
			{
				"criterionId": 221,
				"satisfied": 1,
				"comment": "Exactly right."
			},

			{
				"criterionId": 231,
				"satisfied": 1,
				"comment": "Perfect."
			},

			{
				"criterionId": 241,
				"satisfied": 1,
				"comment": "Amazing."
			}
		]
	},

	"approved": true
}

You can choose whichever data items you need to proceed with the earner's application. In the sample code above, we include the badge slug plus earner email, in a URL the earner will be able to click on in the email. When this happens you could create a badge instance through the API, at which point another webhook notification will be sent.

Communicating with the Earner

You can send an email or communicate with the earner using a method of your choice. The following simplified code demonstrates a basic email function:

var mail = require("nodemailer").mail;
mail({
	from: "Badge Issuer <badges@issuer.com>", //your email
	to: emailTo,
	subject: "Badge", //your subject
	generateTextFromHTML: true,
	html: info
});

The code retrieves the earner email from the review data received, sending the email to the address associated with the badge application. You can of course configure how much information you want to include in the email and how much you want to keep on your own site.

BadgeKit Web App

If you're using the BadgeKit Web app, you can see how the data items within an application review are packaged in the webhook data. For example, the following screenshots show the two parts of the review interface in which the assessor includes feedback:

Criteria Data in a Review Finish Section in a Review

The data in both of these sections is included in the review object sent to the webhook. The Criteria section data is included in reviewItems and the feedback in the Finish section is included in the comment field. Here is the simple email the sample code above produces:

Review Email

Next Steps

Next up we will build on the process outlined above to create a new badge instance when the earner confirms that they want to accept the badge. See Awarding Badges for an overview of how to do that. This will cause another notification to fire at the webhook, at which point you can again contact the earner, for example if you wish to let them carry out follow-up tasks such as pushing their badge to a backpack.

Clone this wiki locally