-
Notifications
You must be signed in to change notification settings - Fork 46
Awarding Badges
In this guide we will work through the process of actually issuing a badge via BadgeKit API. As you will have seen if you followed the Application Review Webhooks guide, a notification is sent to a URL of your choice when an application is reviewed in BadgeKit. At this point you can communicate with the earner, offering them the badge if their application was successful. The code in this document will show how you can build on what we did in the application review webhook guide to go ahead and award the badge when the user chooses to accept it.
For an explanation of how assessment works in BadgeKit, see Assessment with BadgeKit.
If you aren't using assessment, you should still be able to use the guidance below to issue badges via BadgeKit API.
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.
- Overview
- Creating Badge Instances
- Processing an Application
- Authentication
- Badge Issuing Flow
- Webhook Configuration
- Accepting Badges
- Creating a Badge Instance
- Making a Request
- Processing the Application
- Award Hook
Below is a detailed guide to using the API in the context of a node.js app - here's the short version!
POST /systems/:slug/badges/:slug/instances
POST /systems/:slug/issuers/:slug/badges/:slug/instances
POST /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/instances
See the API docs for more on the badge issuing endpoints.
{
"email": "[email protected]"
}
All you need to issue a badge is the earner email address - BadgeKit API will retrieve the badge data from the endpoint path you request. Here are the data items you can post:
NAME | VALUE |
---|---|
email |
required - earner email address |
slug |
optional - instance slug (API will generate if not provided) |
issuedOn |
optional - date issued (API will generate if not provided) |
expires |
optional - expiry date (unix time) |
claimCode |
optional - use only when issuing via claim code |
The API returns status
and instance
objects when a badge instance is successfully created:
{
"status": "created",
"instance":
{
"slug": "abcde12345",
"email": "[email protected]",
"expires": null,
"issuedOn": "2014-05-13T16:29:13.911Z",
"assertionUrl": "http://issuersite.com/public/assertions/abcde12345"
}
}
When you create a badge instance for an earner email address, BadgeKit will NOT communicate with the earner. Instead, the API sends notification to your webhook URL - you can then contact the earner regarding their badge.
PUT /systems/:slug/badges/:slug/applications/:slug
PUT /systems/:slug/issuers/:slug/badges/:slug/applications/:slug
PUT /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug
See the API docs for more on the application update endpoints.
{
"processed": "processed date"
}
All you need to process an application is the processed field - BadgeKit API will retrieve the application data from the endpoint path you request. You can optionally also include an evidence
array or alternatively the application
object itself (which can include both the processed
and evidence
values together with other application data items).
The API returns status
and application
objects when an application is successfully marked as processed:
{
"status": "updated",
"application":
{
"id": 1,
"slug": "abcdef123456",
"learner": "[email protected]",
"created": "2014-05-06T12:24:45.000Z",
"assignedTo": null,
"assignedExpiration": null,
"badge":
{
"id": 1,
"slug": "great-badge",
"name": "Great Badge",
"strapline": "General greatness.",
"earnerDescription": "This badge shows how great you are.",
"consumerDescription": "Earners of this badge are 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": "systemsite.com",
"name": "Excellent System",
"email": null,
"imageUrl": null
},
"criteriaUrl": "http://issuersite.com/system/badgekit/badge/great-badge/criteria",
"criteria":
[
{
"id": 1,
"description": "Is really great.",
"required": 1,
"note": ""
},
{
"id": 2,
"description": "Demonstrates greatness.",
"required": 1,
"note": ""
}
],
"categories":
[
]
},
"processed": null,
"evidence":
[
{
"url": null,
"mediaType": null,
"reflection": "I did great stuff."
}
]
}
}
Requests should be signed using your BadgeKit API secret and a JWT token in the Authorization header. See the Authorization doc for more.
For a more detailed overview of awarding a badge by creating a badge instance and marking an application as processed, read on.
BadgeKit API is designed to let you choose an issuing flow that suits your own community of earners. The following is a typical overview, although you may not need to implement all of these stages in your own situation:
- earner applies for badge
- badge application is reviewed
- review data is sent to webhook URL
- webhook emails earner, offering badge if application was a success
- earner chooses to accept the badge via the issuer site*
- issuer site awards the badge:
- creates a badge instance*
- marks the application as processed*
- award data is sent to the webhook
- issuer communicates with the earner
- for example offering to push their new badge to a backpack
*In this guide we will demonstrate these parts of the process.
If you have not done so already, you need to configure a webhook for your BadgeKit instance by inserting a record in the API webhooks
table. Include the address at which you plan on processing the webhook notification, a secret to carry out authentication and the ID for the system the webhook is associated with. See the following example:
INSERT INTO 'webhooks' ('id', 'url', 'secret', 'systemId') VALUES (null, 'http://issuersite.com/hook', 'asecret', 1);
If you followed the application review webhook guide, you will be able to build what we do in this guide into your existing code. The samples below require the following resources:
var http = require("http");
var jws = require('jws');
var bodyParser = require('body-parser');
app.use(bodyParser.json());
var crypto = require("crypto");
var qs = require("qs");
When an application review assesses an earner as having met the criteria to earn a badge, BadgeKit classifies the application as approved
. At this point in the review webhook code, we offered the earner the badge by including a link in their email message - you can opt to carry this out any way you prefer, for example within your own site. The link we included in the email had the following structure:
http://issuersite.com/accept?badge=a-great-badge&[email protected]&application=1a2b3c4d5e6f7g8h9i0j
For simplicity, we will process the data in the node app by retrieving the URL parameters in the accept
function. To issue the badge, we need the badge slug, earner email and application slug. With the badge slug and earner email, we can create a badge instance to represent the specific instance badge awarded to the earner. With the application slug, we will mark the earner's application for the badge as processed. This will stop the application from appearing in the Applications > Pending list in the BadgeKit Web app if you are using it.
Creating the badge instance and marking the application as processed both involve BadgeKit API endpoints.
Let's start by creating a badge instance when the earner has chosen to accept the badge. In our node app, we represent the URL included in the earner's email message as follows (ending /accept
):
app.get('/accept', function(req, res) {
//issue the badge
});
Inside this function, first retrieve the parameters we included in the URL:
var badgeSlug=req.param("badge");
var earner=req.param("earner");
var application=req.param("application");
The endpoints for creating a badge instance are as follows:
POST /systems/system-slug/badges/badge-slug/instances
POST /systems/system-slug/issuers/issuer-slug/badges/badge-slug/instances
POST /systems/system-slug/issuers/issuer-slug/programs/program-slug/badges/badge-slug/instances
See the API docs for more on the badge issuing endpoints.
In the sample code, the badge simply belongs to a system, so we can define the path as follows:
var awardPath = "/systems/badgekit/badges/"+badgeSlug+"/instances";
This would apply where the system slug is badgekit
.
BadgeKit API will deduce the badge details from the endpoint path, so the only data item we need to pass to create a badge instance is the earner email:
var awardData = qs.stringify({
email: earner
});
We can prepare the request data to call on the API as follows:
var claimData = {
header: {typ: 'JWT', alg: 'HS256'},
payload: {
key: 'master',
exp: Date.now() + (1000 * 60),
method: 'POST',
path: awardPath,
body: {
alg: "SHA256",
hash: crypto.createHash('SHA256').update(awardData).digest('hex')
}
},
secret: 'yoursecret'
};
var requestOptions = {
host : 'yourapilocation.com',
path : awardPath,
method : 'POST',
headers: { 'Authorization': 'JWT token="' + jws.sign(claimData) + '"',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(awardData)
}
};
As usual, your path and method must match in these two objects or authentication will fail. Include your own API location and secret. When you attempt to create a badge instance, the response data will have the following structure:
{
"status": "created",
"instance":
{
"slug": "abcdefghijk1234567890",
"email": "[email protected]",
"expires": null,
"issuedOn": "2014-05-13T16:29:13.911Z",
"assertionUrl": "http://issuersite.com/public/assertions/abcdefghijk1234567890"
}
}
You can therefore detect success by checking the status
field includes created
. You can make the post request as follows:
var postRequest = http.request(requestOptions, function(acceptResponse) {
var response = [];
acceptResponse.setEncoding('utf8');
acceptResponse.on('data', function (responseData) {
response.push(responseData);
});
acceptResponse.on('end', function(){
var awardData=JSON.parse(response.join(''));
console.log('accept response: ' + response.join(''));//output all at once
if(awardData.status==="created"){ //status created
res.send("<html>"+
"<head>"+
"<title>Badge Accepted</title>"+
"<style type='text/css'>"+
"//add some css"+
"</style>"+
"</head>"+
"<body>"+
"<h1>You've accepted your badge!</h1>"+
"<p>Thanks! We'll be in touch.</p>"+
"</body>"+
"</html>"
);
//mark application as processed
processApplication(badgeSlug, application);//we will add this next
}
else {
res.send("Whoops! Something went wrong with your badge.");
}
});
});
postRequest.on('error', function(e) {
console.error(e);
});
// post the data
postRequest.write(awardData);
postRequest.end();
We check the status
is created
and output a confirmation to the user - here is the simple page with a little CSS added:
Note that you can issue a particular badge to multiple earner email addresses at one time using the bulk issuing route:
POST /systems/system-slug/badges/badge-slug/instances/bulk
POST /systems/system-slug/issuers/issuer-slug/badges/badge-slug/instances/bulk
POST /systems/system-slug/issuers/issuer-slug/programs/program-slug/badges/badge-slug/instances/bulk
See the API docs for more on the bulk issuing endpoints.
Your post data should include an array of the earner email addresses you are awarding the badge to:
var awardData = qs.stringify({
emails: ['[email protected]', '[email protected]']
});
The API will only create instances for email addresses that do not already have an awarded instance of the badge. Any duplicate email addresses will only be awarded the badge once. The API responds to calls at the bulk issuing route with an array of the created badge instances.
Now we've created the badge instance, but we still have to mark the application in the API database as processed. If you use the BadgeKit Web app, you will notice that pending applications persist even after review - applications continue to appear until they are marked as processed. Notice that in the above code we included the following line:
processApplication(badgeSlug, application);
Let's add this method next:
function processApplication(bdgSlug, appSlug){
//process application
}
We pass the badge and application slug, so inside the function we can build these into the path:
var processPath = "/systems/badgekit/badges/"+bdgSlug+"/applications/"+appSlug;
The endpoint for updating an application is as follows:
PUT /systems/system-slug/badges/badge-slug/applications/application-slug
PUT /systems/system-slug/issuers/issuer-slug/badges/badge-slug/applications/application-slug
PUT /systems/system-slug/issuers/issuer-slug/programs/program-slug/badges/badge-slug/applications/application-slug
See the API docs for more on the application update endpoints.
All we need to pass as data to this endpoint is a processed
field, which will be a date:
var proc = new Date();
var processData = qs.stringify({
processed: proc
});
Now we can prepare the request data as before:
var claimData = {
header: {typ: 'JWT', alg: 'HS256'},
payload: {
key: 'master',
exp: Date.now() + (1000 * 60),
method: 'PUT',
path: processPath,
body: {
alg: "SHA256",
hash: crypto.createHash('SHA256').update(processData).digest('hex')
}
},
secret: 'yoursecret'
};
var requestOptions = {
host : 'yourapilocation.com',
path : processPath,
method : 'PUT',
headers: { 'Authorization': 'JWT token="' + jws.sign(claimData) + '"',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(processData)
}
};
This time it is a put request as we are updating the data for an existing application. Now we can make the request:
var putRequest = http.request(requestOptions, function(processResponse) {
var response = [];
processResponse.setEncoding('utf8');
processResponse.on('data', function (responseData) {
response.push(responseData);
});
processResponse.on('end', function(){
console.log("process response: "+response.join(''));
});
});
putRequest.on('error', function(e) {
console.error(e);
});
// post the data
putRequest.write(processData);
putRequest.end();
We simply write the result to the console - in your own site you may wish to carry out additional steps. The response has the following structure:
{
"status": "updated",
"application":
{
"id": 11,
"slug": "abcdefghijk1234567890",
"learner": "[email protected]",
"created": "2014-05-14T19:11:19.000Z",
"assignedTo": null,
"assignedExpiration": null,
"badge":
{
"id": 11,
"slug": "dowell-and-co-new-group-leader",
"name": "DoWell and Co New Group Leader",
"strapline": "The Group Leader badge acknowledges dynamic, effective leadership amongst peers in the realm of program facilitation.",
"earnerDescription": "The Group Leader badge communicates that you are a leader amongst your peers. With it you demonstrate that you have effectively performed at least four of the following skills at DoWell and Co - communication, delegation, organization, responsibility & ownership, and passionate commitment.",
"consumerDescription": "The DoWell & Co Group Leadership badge communicates important leadership values that we hold dear at DoWell & Co. Values that will serve an earner throughout their life. This badge represents breadth and depth of commitment to at least four of the five necessary leadership skills: communication, delegation, organization, responsibility & ownership, and passionate commitment. Along with the other DoWell & Co badges, particularly Group Management and Time Management, the earning of this badge acknowledges exemplary achievement of the highest sort at DoWell & Co for groups of 7-12 members.",
"issuerUrl": "dowellandco.co",
"rubricUrl": "",
"timeValue": 0,
"timeUnits": "minutes",
"limit": 0,
"unique": 0,
"created": "2014-05-14T17:34:16.000Z",
"imageUrl": "http://issuersite.com/images/badge/251",
"type": "",
"archived": false,
"system":
{
"id": 1,
"slug": "badgekit",
"url": "http://systemsite.com",
"name": "Best System",
"email": null,
"imageUrl": null,
"issuers":
[
]
},
"criteriaUrl": "http://issuersite.com/system/badgekit/badge/dowell-and-co-new-group-leader/criteria",
"criteria":
[
{
"id": 31,
"description": "The Group Leader badge recognizes achievement in the realm of program facilitation. It acknowledges dynamic, effective interaction with peers in the completion of a group project. Group Leaders possess some form of at least four of the following five skills: superior communication skills, effective delegation skills, advanced organization skills, personal responsibility & ownership skills, passionate commitment.",
"required": 1,
"note": ""
},
{
"id": 41,
"description": "This badge illustrates expertise in reaching goals involving complex collaborative environments of groups ranging in size between 7 and 12 members.",
"required": 0,
"note": ""
}
],
"categories":
[
],
"tags":
[
]
},
"processed": "2014-05-14T19:11:53.000Z",
"evidence":
[
{
"url": null,
"mediaType": null,
"reflection": "I lead people and stuff."
},
{
"url": "http://mysites.com/app/uploads/picture.png",
"mediaType": "image",
"reflection": "picture"
},
{
"url": "http://site.com/myevidence.html",
"mediaType": "link",
"reflection": null
}
]
}
}
As you can see, the status
is updated
and the processed
field has a date in it.
When you create a badge instance as we do above, another notification is sent to the webhook. You can configure your webhook code to again follow up with the earner when this occurs - see Badge Issued Webhooks for an overview of how to do that.
For support working with BadgeKit or Open Badges, use any of the following channels:
- Post general questions in our Community Google Group and post technical questions in our Dev Google Group.
- Reach members of the Open Badges team directly on IRC (irc.mozilla.org) on the #badges channel.
- Email questions directly to [email protected] and a member of the team will follow-up.
- Follow or tweet the Open Badges team @OpenBadges.
- Get involved or submit issues via the GitHub repos - feedback is always appreciated!