diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..fc5bc35 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,36 @@ +# BadgeKit API Documentation + +The BadgeKit API docs include the information you need to get started using the endpoints and webhooks. The docs are structured as follows: + +* [API Endpoints](api-endpoints.md) + * Containers: + * [Systems](systems.md) + * [Issuers](issuers.md) + * [Programs](programs.md) + * Badge Management: + * [Badges](badges.md) + * [Milestones](milestones.md) + * [Claim Codes](claim-codes.md) + * [Issuing](issuing.md) + * [Assessment](assessment.md) +* [Webhooks](webhooks.md) +* [Authorization](authorization.md) + +You can interact with badge and application data managed by the API using the endpoints. The data you send to the API endpoints needs to be signed for authentication, and the data you receive from the API (in responses and webhook messages) is signed before it is sent. To detect badging events carried out through the API, such as badges being issued and badge applications being reviewed, you can configure a webhook URL to which BadgeKit API will send data. + +The API docs provide a reference for the endpoints and webhooks. You will also find detailed guides to carrying out common processes, including the assessment flow, together with sample code excerpts, in the [BadgeKit API wiki](https://github.com/mozilla/badgekit-api/wiki): + +* [Using BadgeKit API](https://github.com/mozilla/badgekit-api/wiki/Using-BadgeKit-API) +* [Retrieving Badges](https://github.com/mozilla/badgekit-api/wiki/Retrieving-Badges) +* [Submitting Applications](https://github.com/mozilla/badgekit-api/wiki/Submitting-Applications) +* [Application Review Webhooks](https://github.com/mozilla/badgekit-api/wiki/Application-Review-Webhooks) +* [Awarding Badges](https://github.com/mozilla/badgekit-api/wiki/Awarding-Badges) +* [Badge Issued Webhooks](https://github.com/mozilla/badgekit-api/wiki/Badge-Issued-Webhooks) + +For additional support using BadgeKit or the API, feel free to get in touch using one of the following methods: + +* Post general questions in our [Community Google Groups](http://bit.ly/OBIGeneral) and post technical questions in our [Dev Google Group](http://bit.ly/OBIDev). +* Reach members of the Open Badges team directly on IRC (irc.mozilla.org) on the #badges channel. +* Email questions directly to [badges@mozillafoundation.org](mailto:badges@mozillafoundation.org) and a member of the team will follow-up. +* Follow or tweet the Open Badges team [@OpenBadges](https://twitter.com/OpenBadges). +* Get involved or submit issues via the GitHub repos - feedback is always appreciated! diff --git a/docs/api-endpoints.md b/docs/api-endpoints.md index aa80bc2..4c19411 100644 --- a/docs/api-endpoints.md +++ b/docs/api-endpoints.md @@ -1,24 +1,28 @@ +# API Endpoints + +See the following overview of the available BadgeKit API endpoints - browse to the linked docs in each section for more detailed information. + * Containers - * Systems + * [Systems](systems.md) * **GET** /systems * **POST** /systems * **GET** /systems/:slug * **PUT** /systems/:slug * **DELETE** /systems/:slug - * Issuers + * [Issuers](issuers.md) * **GET** /systems/:slug/issuers * **POST** /systems/:slug/issuers * **GET** /systems/:slug/issuers/:slug * **PUT** /systems/:slug/issuers/:slug * **DELETE** /systems/:slug/issuers/:slug - * Programs + * [Programs](programs.md) * **GET** /systems/:slug/issuers/:slug/programs * **POST** /systems/:slug/issuers/:slug/programs * **GET** /systems/:slug/issuers/:slug/programs/:slug * **PUT** /systems/:slug/issuers/:slug/programs/:slug * **DELETE** /systems/:slug/issuers/:slug/programs/:slug -* Badges - * Managing: Badges can belong directly to a system, an issuer, or a program. +* Badge Management + * [Badges](badges.md) (can belong directly to a system, issuer or program) * **GET** /systems/:slug/badges * **GET** /systems/:slug/issuers/:slug/badges * **GET** /systems/:slug/issuers/:slug/programs/:slug/badges @@ -34,10 +38,7 @@ * **DELETE** /systems/:slug/badges/:slug * **DELETE** /systems/:slug/issuers/:slug/badges/:slug * **DELETE** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug - * Claim Codes - * **GET** /systems/:slug/codes - * **GET** /systems/:slug/issuers/:slug/codes - * **GET** /systems/:slug/issuers/:slug/programs/:slug/codes + * [Claim Codes](claim-codes.md) * **GET** /systems/:slug/codes/:code * **GET** /systems/:slug/issuers/:slug/codes/:code * **GET** /systems/:slug/issuers/:slug/programs/:slug/codes/:code @@ -59,10 +60,7 @@ * **POST** /systems/:slug/badges/:slug/codes/:code/claim * **POST** /systems/:slug/issuers/:slug/badges/:slug/codes/:code/claim * **POST** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/codes/:code/claim - * **POST** /systems/:slug/badges/:slug/codes/:code/unclaim - * **POST** /systems/:slug/issuers/:slug/badges/:slug/codes/:code/unclaim - * **POST** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/codes/:code/unclaim - * Issuing + * [Issuing](issuing.md) (badge instances) * **GET** /systems/:slug/instances/:email * **GET** /systems/:slug/issuers/:slug/instances/:email * **GET** /systems/:slug/issuers/:slug/programs/:slug/instances/:email @@ -78,7 +76,7 @@ * **DELETE** /systems/:slug/badges/:slug/instances/:email * **DELETE** /systems/:slug/issuers/:slug/badges/:slug/instances/:email * **DELETE** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/instances/:email - * Assessment + * [Assessment](assessment.md) (managing earner applications for badges) * **GET** /systems/:slug/applications * **GET** /systems/:slug/issuers/:slug/applications * **GET** /systems/:slug/issuers/:slug/programs/:slug/applications @@ -88,30 +86,43 @@ * **POST** /systems/:slug/badges/:slug/applications * **POST** /systems/:slug/issuers/:slug/badges/:slug/applications * **POST** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications - * **GET** /systems/:slug/badges/:slug/applications/:id - * **GET** /systems/:slug/issuers/:slug/badges/:slug/applications/:id - * **GET** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:id - * **GET** /systems/:slug/badges/:slug/applications/:id/evidence - * **GET** /systems/:slug/issuers/:slug/badges/:slug/applications/:id/evidence - * **GET** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:id/evidence - * **POST** /systems/:slug/badges/:slug/applications/:id/evidence - * **POST** /systems/:slug/issuers/:slug/badges/:slug/applications/:id/evidence - * **POST** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:id/evidence - * **GET** /systems/:slug/badges/:slug/applications/:id/evidence/:id - * **GET** /systems/:slug/issuers/:slug/badges/:slug/applications/:id/evidence/:id - * **GET** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:id/evidence/:id - * **DELETE** /systems/:slug/badges/:slug/applications/:id/evidence/:id - * **DELETE** /systems/:slug/issuers/:slug/badges/:slug/applications/:id/evidence/:id - * **DELETE** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:id/evidence/:id - * **GET** /systems/:slug/badges/:slug/applications/:id/reviews/:id - * **GET** /systems/:slug/issuers/:slug/badges/:slug/applications/:id/reviews/:id - * **GET** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:id/reviews/:id - * **POST** /systems/:slug/badges/:slug/applications/:id/reviews - * **POST** /systems/:slug/issuers/:slug/badges/:slug/applications/:id/reviews - * **POST** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:id/reviews - * **PUT** /systems/:slug/badges/:slug/applications/:id/reviews/:id - * **PUT** /systems/:slug/issuers/:slug/badges/:slug/applications/:id/reviews/:id - * **PUT** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:id/reviews/:id - * **DELETE** /systems/:slug/badges/:slug/applications/:id/reviews/:id - * **DELETE** /systems/:slug/issuers/:slug/badges/:slug/applications/:id/reviews/:id - * **DELETE** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:id/reviews/:id + * **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 + * **GET** /systems/:slug/badges/:slug/applications/:slug + * **GET** /systems/:slug/issuers/:slug/badges/:slug/applications/:slug + * **GET** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug + * **DELETE** /systems/:slug/badges/:slug/applications/:slug + * **DELETE** /systems/:slug/issuers/:slug/badges/:slug/applications/:slug + * **DELETE** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug + * **GET** /systems/:slug/badges/:slug/applications/:slug/reviews + * **GET** /systems/:slug/issuers/:slug/badges/:slug/applications/:slug/reviews + * **GET** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug/reviews + * **GET** /systems/:slug/badges/:slug/applications/:slug/reviews/:slug + * **GET** /systems/:slug/issuers/:slug/badges/:slug/applications/:slug/reviews/:slug + * **GET** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug/reviews/:slug + * **POST** /systems/:slug/badges/:slug/applications/:slug/reviews + * **POST** /systems/:slug/issuers/:slug/badges/:slug/applications/:slug/reviews + * **POST** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug/reviews + * **PUT** /systems/:slug/badges/:slug/applications/:slug/reviews/:slug + * **PUT** /systems/:slug/issuers/:slug/badges/:slug/applications/:slug/reviews/:slug + * **PUT** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug/reviews/:slug + * **DELETE** /systems/:slug/badges/:slug/applications/:slug/reviews/:slug + * **DELETE** /systems/:slug/issuers/:slug/badges/:slug/applications/:slug/reviews/:slug + * **DELETE** /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug/reviews/:slug + * [Milestones](milestones.md) + * **GET** /systems/:slug/milestones + * **POST** /systems/:slug/milestones + * **GET** /systems/:slug/milestones/:milestoneId + * **PUT** /systems/:slug/milestones/:milestoneId + * **DELETE** /systems/:slug/milestones/:milestoneId + +See also [authorization](authorization.md) and [webhooks](webhooks.md). + + diff --git a/docs/assessment.md b/docs/assessment.md new file mode 100644 index 0000000..6579787 --- /dev/null +++ b/docs/assessment.md @@ -0,0 +1,860 @@ +# Assessment + +Badges can be issued following assessment of earner applications. Issuers can allow earners to submit applications for badges, forwarding these applications to the API. Reviewers can then assess pending applications, making awarding decisions and submitting their reviews. When a review is submitted, issuers can detect this at their [webhook](webhooks.md). Typically an issuer will respond to an approved review by offering the earner the badge, creating a [badge instance](issuing.md) and marking the application as processed using the updating endpoint below. + +Assessment therefore involves two objects in BadgeKit API: applications and reviews. + +## Applications + +| NAME | VALUE | +|:---|:---| +| `id` | __integer__ - _id from database_ | +| `slug` | __string__ | +| `learner` | __email address__ - _earner email_ | +| `created` | __timestamp__ | +| `assignedTo` | __string__ - _email login for assigned reviewer_ | +| `assignedExpiration` | __timestamp__ | +| `badge` | [badge](badges.md) - _badge applied for_ | +| `processed` | __timestamp__ - _e.g. set when review is submitted or when badge instance is created_ | +| `evidence` | __array__ - _each evidence item can include: `url`, `mediaType` (which can be `image` or `link`) and `reflection` (which is a string)_ | + +## Reviews + +| NAME | VALUE | +|:---|:---| +| `id` | __integer__ - _id from database_ | +| `slug` | __string__ | +| `author` | __email address__ - _reviewer email_ | +| `comment` | __string__ - _applicant feedback_ | +| `reviewItems` | __array__ - _one for each criteria item in the badge; each reviewItem can include: `criterionId`, `satisfied` status and `comment`_ | +| `approved` | __boolean__ - _indicates success of application_ | + +## Endpoints + +* [Retrieve Applications](#retrieve-applications) + * `GET /systems/:slug/applications` + * `GET /systems/:slug/issuers/:slug/applications` + * `GET /systems/:slug/issuers/:slug/programs/:slug/applications` + * `GET /systems/:slug/badges/:slug/applications` + * `GET /systems/:slug/issuers/:slug/badges/:slug/applications` + * `GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications` +* [Retrieve a Specific Application](#retrieve-a-specific-application) + * `GET /systems/:slug/badges/:slug/applications/:slug` + * `GET /systems/:slug/issuers/:slug/badges/:slug/applications/:slug` + * `GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug` +* [Submit an Application](#submit-an-application) + * `POST /systems/:slug/badges/:slug/applications` + * `POST /systems/:slug/issuers/:slug/badges/:slug/applications` + * `POST /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications` +* [Update an Application](#update-an-application) + * `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` +* [Delete an Application](#delete-an-application) + * `DELETE /systems/:slug/badges/:slug/applications/:slug` + * `DELETE /systems/:slug/issuers/:slug/badges/:slug/applications/:slug` + * `DELETE /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug` +* [Retrieve Application Reviews](#retrieve-application-reviews) + * `GET /systems/:slug/badges/:slug/applications/:slug/reviews` + * `GET /systems/:slug/issuers/:slug/badges/:slug/applications/:slug/reviews` + * `GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug/reviews` +* [Retrieve a Specific Review](#retrieve-a-specific-review) + * `GET /systems/:slug/badges/:slug/applications/:slug/reviews/:slug` + * `GET /systems/:slug/issuers/:slug/badges/:slug/applications/:slug/reviews/:slug` + * `GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug/reviews/:slug` +* [Submit an Application Review](#submit-an-application-review) + * `POST /systems/:slug/badges/:slug/applications/:slug/reviews` + * `POST /systems/:slug/issuers/:slug/badges/:slug/applications/:slug/reviews` + * `POST /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug/reviews` +* [Update a Review](#update-a-review) + * `PUT /systems/:slug/badges/:slug/applications/:slug/reviews/:slug` + * `PUT /systems/:slug/issuers/:slug/badges/:slug/applications/:slug/reviews/:slug` + * `PUT /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug/reviews/:slug` +* [Delete a Review](#delete-a-review) + * `DELETE /systems/:slug/badges/:slug/applications/:slug/reviews/:slug` + * `DELETE /systems/:slug/issuers/:slug/badges/:slug/applications/:slug/reviews/:slug` + * `DELETE /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug/reviews/:slug` + +## Retrieve Applications + +Retrieve existing applications within a system, issuer or program or for a specific badge. + +### Expected request + +``` +GET /systems/:slug/applications +GET /systems/:slug/issuers/:slug/applications +GET /systems/:slug/issuers/:slug/programs/:slug/applications +GET /systems/:slug/badges/:slug/applications +GET /systems/:slug/issuers/:slug/badges/:slug/applications +GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications +``` + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "applications": [ + { + "id": 1, + "slug": "application-slug", + "learner": "earner@adomain.com", + "created": "2014-05-29T18:24:59.000Z", + "assignedTo": null, + "assignedExpiration": null, + "badge": { + ... + }, + "processed": null, + "evidence": [ + { + "url": null, + "mediaType": null, + "reflection": "I did things relevant to the badge..." + }, + { + "url": "http://issuersite.com/uploaded-image.jpg", + "mediaType": "image", + "reflection": "A picture of my evidence." + }, + { + "url": "http://awebsite.com/evidence.html", + "mediaType": "link", + "reflection": "My website where I did things." + } + ] + }, + ... + ] +} +``` + +#### Response structure + +* applications `[ ]` + * id + * slug + * learner + * created + * assignedTo + * assignedExpiration + * [badge](badges.md) + * processed + * evidence `[ ]` + * url + * mediaType + * reflection + +### Potential errors + +*None* + +## Retrieve a Specific Application + +Retrieve the details for a specific application using its slug. + +### Expected request + +``` +GET /systems/:slug/badges/:slug/applications/:slug +GET /systems/:slug/issuers/:slug/badges/:slug/applications/:slug +GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug +``` + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "application": + { + "id": 1, + "slug": "application-slug", + "learner": "earner@adomain.com", + "created": "2014-05-29T18:24:59.000Z", + "assignedTo": null, + "assignedExpiration": null, + "badge": { + ... + }, + "processed": null, + "evidence": [ + { + "url": null, + "mediaType": null, + "reflection": "I did things relevant to the badge..." + }, + { + "url": "http://issuersite.com/uploaded-image.jpg", + "mediaType": "image", + "reflection": "A picture of my evidence." + }, + { + "url": "http://awebsite.com/evidence.html", + "mediaType": "link", + "reflection": "My website where I did things." + } + ] + }, + ... + ] +} +``` + +#### Response structure + +* application + * id + * slug + * learner + * created + * assignedTo + * assignedExpiration + * [badge](badges.md) + * processed + * evidence `[ ]` + * url + * mediaType + * reflection + +### Potential errors + +* **Application not found** + +``` +HTTP/1.1 404 Not Found +Content-Type: application/json +``` + +```json +{ + "code": "ResourceNotFound", + "message": "Could not find application field: `slug`, value: " +} +``` + +## Submit an Application + +Post an earner application for a badge. _If you're using the BadgeKit Web app, submitted applications appear there for review._ + +### Expected request + +``` +POST /systems/:slug/badges/:slug/applications +POST /systems/:slug/issuers/:slug/badges/:slug/applications +POST /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications +``` + +Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. + +| Parameters | Required | Description | +|:-----------------------|-----------------|--------------------------| +| **learner** | required | The email address for the earner applying. | +| **evidence** | optional | Array including evidence items - each item can include `reflection`, `mediaType` and `url`. | +| **assignedTo** | optional | Email of reviewer application is assigned to. | +| **assignedExpiration** | optional | Expiry date. | + +### Expected response + +``` +HTTP/1.1 201 Created +Content-Type: application/json +``` + +```json +{ + "status": "created", + "application": + { + "id": 1, + "slug": "abcdef123456", + "learner": "earner@example.com", + "created": "2014-05-06T12:24:45.000Z", + "assignedTo": null, + "assignedExpiration": null, + "badge": + { + ... + }, + + "processed": null, + "evidence": + [ + { + "url": "http://awebsite.com/page", + "mediaType": "link", + "reflection": "I did great stuff." + }, + ... + ] + } +} +``` + +## Response structure + +* status +* application + * id + * slug + * learner + * created + * assignedTo + * assignedExpiration + * [badge](badges.md) + * processed + * evidence `[ ]` + * url + * mediaType + * reflection + +#### Potential Errors + +* **Invalid data** + +``` + HTTP/1.1 400 Bad Request + Content-Type: application/json +``` + +```json + { + "code": "ValidationError", + "message": "Could not validate required fields", + "details": [ + { + "field": "learner", + "value": "..." + }, + ... + ] + } +``` + +## Update an Application + +Update an existing application - a typical use of this endpoint would be to mark an application as processed, for example following review and badge issuing. + +### Expected request + +``` +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 +``` + +Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. + +| Parameters | Description | +|:-----------------------|--------------------------| +| **learner** | The email address for the earner applying. | +| **evidence** | Array including evidence items - each item can include `reflection`, `mediaType` and `url`. | +| **assignedTo** | Email of reviewer application is assigned to. | +| **assignedExpiration** | Expiry date. | +| **processed** | Timestamp indicating application has been processed. | + +You only have to pass in the fields you are updating. Any fields that are not represented will be left unchanged. + +### Expected response + + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "status": "updated", + "application": + { + "id": 1, + "slug": "abcdef123456", + "learner": "earner@example.com", + "created": "2014-05-06T12:24:45.000Z", + "assignedTo": null, + "assignedExpiration": null, + "badge": + { + ... + }, + + "processed": "2014-05-06T12:24:45.000Z", + "evidence": + [ + { + "url": "http://awebsite.com/page", + "mediaType": "link", + "reflection": "I did great stuff." + } + ] + } +} +``` + +#### Response structure + +* status +* application + * id + * slug + * learner + * created + * assignedTo + * assignedExpiration + * [badge](badges.md) + * processed + * evidence `[ ]` + * url + * mediaType + * reflection + +### Potential errors + +* **Invalid data** + +``` + HTTP/1.1 400 Bad Request + Content-Type: application/json +``` + +```json + { + "code": "ValidationError", + "message": "Could not validate required fields", + "details": [ + { + "message": "Invalid email", + "field": "learner", + "value": "..." + }, + ... + ] + } +``` + +## Delete an Application + +Delete an existing application. + +### Expected request + +``` +DELETE /systems/:slug/badges/:slug/applications/:slug +DELETE /systems/:slug/issuers/:slug/badges/:slug/applications/:slug +DELETE /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug +``` + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "status": "deleted", + "application": { + "id": 1, + "slug": "abcde12345", + "learner": "earner@adomain.com", + "created": "2014-05-29T18:24:59.000Z", + "assignedTo": null, + "assignedExpiration": null, + "badge": null, + "processed": null, + "evidence": [ ] + } + +} +``` + +### Potential errors + +* **Application not found** + +``` + HTTP/1.1 404 Not Found + Content-Type: application/json +``` + +```json + { + "code": "ResourceNotFound", + "message": "Could not find application field: `slug`, value: " + } +``` + +## Retrieve Application Reviews + +Retrieve reviews for specific applications. + +### Expected request + +``` +GET /systems/:slug/badges/:slug/applications/:slug/reviews +GET /systems/:slug/issuers/:slug/badges/:slug/applications/:slug/reviews +GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug/reviews +``` + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "reviews": [ + { + "id": 1, + "slug": "abcde12345", + "author": "reviewer@issuersite.com", + "comment": "fantastic work", + "reviewItems": [ + { + "criterionId": 1, + "satisfied": 1, + "comment": "perfect" + }, + ... + ] + }, + ... + ] +} +``` + +#### Response structure + +* reviews `[ ]` + * id + * slug + * author + * comment + * reviewItems `[ ]` + * criterionId + * satisfied + * comment + +### Potential errors + +*None* + +## Retrieve a Specific Review + +Retrieve a specific application review. + +### Expected request + +``` +GET /systems/:slug/badges/:slug/applications/:slug/reviews/:slug +GET /systems/:slug/issuers/:slug/badges/:slug/applications/:slug/reviews/:slug +GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug/reviews/:slug +``` + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "review": + { + "id": 1, + "slug": "abcde12345", + "author": "reviewer@issuersite.com", + "comment": "fantastic work", + "reviewItems": [ + { + "criterionId": 1, + "satisfied": 1, + "comment": "perfect" + }, + ... + ] + } +} +``` + +#### Response structure + +* reviews + * id + * slug + * author + * comment + * reviewItems `[ ]` + * criterionId + * satisfied + * comment + +### Potential errors + +* **Review not found** + +``` + HTTP/1.1 404 Not Found + Content-Type: application/json +``` + +```json + { + "code": "ResourceNotFound", + "message": "Could not find review field: `slug`, value " + } +``` + +## Submit an Application Review + +Post a review for a specific application. + +### Expected request + +``` +POST /systems/:slug/badges/:slug/applications/:slug/reviews +POST /systems/:slug/issuers/:slug/badges/:slug/applications/:slug/reviews +POST /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug/reviews +``` + +Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. + +| Parameters | Required | Description | +|:-----------------------|-----------------|--------------------------| +| **author** | required | Email address of reviewer. +| **comment** | optional | Feedback for earner. +| **reviewItems** | optional | Array, each item includes `criterionId`, `satisfied` and `comment`. + +### Expected response + + +``` +HTTP/1.1 201 Created +Content-Type: application/json +``` + +```json +{ + "status": "created", + "review": { + "id": 1, + "slug": "abcde12345", + "author": "reviewer@issuersite.com", + "comment": "fantastic work", + "reviewItems": [ + { + "criterionId": 1, + "satisfied": 1, + "comment": "perfect" + }, + ... + ] + } + +} +``` + +#### Response structure + +* status +* review + * id + * slug + * author + * comment + * reviewItems `[ ]` + * criterionId + * satisfied + * comment + +### Potential errors + +* **Invalid data** + +``` + HTTP/1.1 400 Bad Request + Content-Type: application/json +``` + +```json + { + "code": "ValidationError", + "message": "Could not validate required fields", + "details": [ + { + "message": "Invalid email", + "field": "author", + "value": "..." + }, + ... + ] + } +``` + +## Update a Review + +Update an existing application review. + +### Expected request + +``` +PUT /systems/:slug/badges/:slug/applications/:slug/reviews/:slug +PUT /systems/:slug/issuers/:slug/badges/:slug/applications/:slug/reviews/:slug +PUT /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug/reviews/:slug +``` + +Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. + +| Parameters | Description | +|:-----------------------|--------------------------| +| **author** | Email address of reviewer. +| **comment** | Feedback for earner. +| **reviewItems** | Array, each item includes `criterionId`, `satisfied` and `comment`. + +You only have to pass in the fields you are updating. Any fields that are not represented will be left unchanged. + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "status": "updated", + "review": { + "id": 1, + "slug": "abcde12345", + "author": "someoneelse@issuersite.com", + "comment": "fantastic work", + "reviewItems": [ + { + "criterionId": 1, + "satisfied": 1, + "comment": "perfect" + }, + ... + ] + } +} +``` + +#### Response structure + +* status +* review + * id + * slug + * author + * comment + * reviewItems `[ ]` + * criterionId + * satisfied + * comment + +### Potential errors + +* **Invalid data** + +``` + HTTP/1.1 400 Bad Request + Content-Type: application/json +``` + +```json + { + "code": "ValidationError", + "message": "Could not validate required fields", + "details": [ + { + "message": "Invalid email", + "field": "author", + "value": "..." + }, + ... + ] + } +``` + +## Delete a Review + +Delete an existing application review. + +### Expected request + +``` +DELETE /systems/:slug/badges/:slug/applications/:slug/reviews/:slug +DELETE /systems/:slug/issuers/:slug/badges/:slug/applications/:slug/reviews/:slug +DELETE /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/applications/:slug/reviews/:slug +``` + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "status": "deleted", + "badge": { + "id": 1, + "slug": "abcde12345", + "author": "reviewer@issuersite.com", + "comment": "fantastic work", + "reviewItems": [ + { + "criterionId": 1, + "satisfied": 1, + "comment": "perfect" + }, + ... + ] + } +} +``` + +#### Response structure + +* status +* review + * id + * slug + * author + * comment + * reviewItems `[ ]` + * criterionId + * satisfied + * comment + +### Potential errors + +* **Review not found** + +``` + HTTP/1.1 404 Not Found + Content-Type: application/json +``` + +```json + { + "code": "ResourceNotFound", + "message": "Could not find review field: `slug`, value: " + } +``` diff --git a/docs/badges.md b/docs/badges.md index a02c06e..9fed932 100644 --- a/docs/badges.md +++ b/docs/badges.md @@ -1,21 +1,70 @@ # Badges -## `Badge List` - -Retrieves all available badges, filtered by system, issuer or program. +A badge represents the generic data for an earnable badge (not an awarded badge, which is a [badge instance](issuing.md)). Badges can be published or archived. Each badge can belong to a [system](systems.md), [issuer](issuers.md) or [program](programs.md). + +| NAME | VALUE | +|:---|:---| +| `id` | __integer__ - _ID from database entry._ | +| `slug` | __string__ - _Used to identify badge in API endpoints._ | +| `name` | __string__ - _Display name._ | +| `strapline` | __string__ - _Short tagline description._ | +| `earnerDescription` | __string__ - _Description for potential earners._ | +| `consumerDescription` | __string__ - _Description for viewers of badge e.g. college admin or employer._ | +| `issuerUrl` | __string__ | +| `rubricUrl` | __string__ - _Link to supporting material._ | +| `timeValue` | __integer__ - _Time estimate for earner to complete badge._ | +| `timeUnits` | __enum__ - _Can be `minutes`, `hours`, `days` or `weeks`._ | +| `limit` | __integer__ - _Limit for number of people who can earn the badge._ | +| `unique` | __boolean__ - _True if the same earner can only earn the badge once._ | +| `created` | __timestamp__ | +| `imageUrl` | __string__ - _Badge display image._ | +| `type` | __string__ - _Badges can be organized by type and category._ | +| `archived` | __boolean__ - _Archived badges can no longer be earned._ | +| `system` | __integer__ - _System is represented by ID in database - system details are returned from API endpoints as nested JSON._ | +| `criteriaUrl` | __string__ - _Link to criteria material._ | +| `criteria` | __array__ - _Each item includes `id`, `description`, `required` status and `note`._ | +| `categories` | __array__ - _See above for related type field._ | +| `tags` | __array__- _Tags can be used to aid search and discovery of badges._ | +| [`milestones`](milestones.md) | __array__ - _A milestone badge is awarded when a set of other badges is earned._ | + +## Endpoints + +* [Retrieve Badge List](#retrieve-badge-list) + * `GET /systems//badges` + * `GET /systems//issuers//badges` + * `GET /systems//issuers//programs//badges` +* [Retrieve Specific Badge](#retrieve-specific-badge) + * `GET /systems//badges/` + * `GET /systems//issuers//badges/` + * `GET /systems//issuers//programs/` +* [Create New Badge](#create-new-badge) + * `POST /systems//badges` + * `POST /systems//issuers//badges` + * `POST / systems//issuers//programs/` +* [Update a Badge](#update-a-badge) + * `PUT /systems//badges/` + * `PUT /systems//issuers//badges/` + * `PUT /systems//issuers//programs//badges/` +* [Delete a Badge](#delete-a-badge) + * `DELETE /systems//badges/` + * `DELETE /systems//issuers//badges/` + * `DELETE /systems//issuers//programs//badges/` + +## Retrieve Badge List + +Retrieves badges, filtered by system, issuer or program. ### Expected request ``` -GET /systems/:systemSlug/badges HTTP/1.1 -GET /systems/:systemSlug/issuers/:issuerSlug/badges HTTP/1.1 -GET /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug/badges HTTP/1.1 +GET /systems/:systemSlug/badges +GET /systems/:systemSlug/issuers/:issuerSlug/badges +GET /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug/badges ``` #### Available request parameters -* **`archived`:** `[true|false|any]` - Whether to include archived badges. +* **`archived`:** `[true|false|any]` (optional) - _Whether to include archived badges._ * `true` will return only archived badges * `false` (default) will return only unarchived badges * `any` will return badges regardless of archived status @@ -25,44 +74,108 @@ GET /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug/badges HTTP/1 ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "badges": [ { - "name": "Badge Name", + "id": 1, "slug": "badge-slug", - "strapline": "Badge Strapline", - "earnerDescription": "Badge Description", - "consumerDescription": "Badge Description for Consumers", - "issuerUrl": "http://example.org/issuer", - "rubricUrl": "http://example.org/rubric", - "criteriaUrl": "http://example.org/criteria", - "timeValue": 10, + "name": "Badge Name", + "strapline": "Badge strapline.", + "earnerDescription": "Badge description for potential earners.", + "consumerDescription": "Badge description for interested consumers.", + "issuerUrl": "http://issuersite.com", + "rubricUrl": "http://issuersite.com/rubric", + "timeValue": 0, "timeUnits": "minutes", "limit": 5, "unique": false, - "imageUrl": "http://example.org/badge.png", - "archived": false + "created": "2014-05-21T19:22:09.000Z", + "imageUrl": "http://issuersite.com/badge.png", + "type": "badge type", + "archived": false, + "system": { + "id": 1, + "slug": "system-slug", + "url": "http://systemsite.com", + "name": "System Name", + "email": "admin@systemsite.com", + "imageUrl": "http://systemsite.com/image.jpg", + "issuers": [ ] + } + "criteriaUrl": "http://issuersite.com/criteria", + "criteria": [ + { + "id": 1, + "description": "Criteria description.", + "required": true, + "note": "Note about criteria for assessor." + }, + ... + ], + "categories": [ ], + "tags": [ ], + "milestones": [ ] }, ... ] } ``` +#### Response structure + +* badges `[ ]` + * id + * slug + * name + * strapline + * earnerDescription + * consumerDescription + * issuerUrl + * rubricUrl + * timeValue + * timeUnits + * limit + * unique + * created + * imageUrl + * type + * archived + * [system](systems.md) `[ ]` + * id + * slug + * url + * name + * email + * imageUrl + * [issuers](issuers.md) `[ ]` + * criteriaUrl + * criteria `[ ]` + * id + * description + * required + * note + * categories `[ ]` + * tags `[ ]` + * [milestones](milestones.md) `[ ]` + + ### Potential errors *None* -## `Retrieve a specific badge` +## Retrieve Specific Badge -Retrieves a specific badge. +Retrieves a specific badge using its slug. ### Expected request ``` -GET /systems/:systemSlug/badges/:badgeSlug HTTP/1.1 -GET /systems/:systemSlug/issuers/:issuerSlug/badges/:badgeSlug HTTP/1.1 -GET /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug/badges/:badgeSlug HTTP/1.1 +GET /systems/:systemSlug/badges/:badgeSlug +GET /systems/:systemSlug/issuers/:issuerSlug/badges/:badgeSlug +GET /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug/badges/:badgeSlug ``` ### Expected response @@ -70,169 +183,243 @@ GET /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug/badges/:badge ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "badge": { - "name": "Badge Name", + "id": 1, "slug": "badge-slug", - "strapline": "Badge Strapline", - "earnerDescription": "Badge Description", - "consumerDescription": "Badge Description for Consumers", - "issuerUrl": "http://example.org/issuer", - "rubricUrl": "http://example.org/rubric", - "criteriaUrl": "http://example.org/criteria", + "name": "Badge Name", + "strapline": "Badge strapline.", + "earnerDescription": "Badge description for potential earners.", + "consumerDescription": "Badge description for consumers.", + "issuerUrl": "http://issuersite.com", + "rubricUrl": "http://issuersite.com/rubric", "timeValue": 10, "timeUnits": "minutes", "limit": 5, "unique": false, - "imageUrl": "http://example.org/badge.png", - "archived": false + "created": "2014-05-21T19:22:09.000Z", + "imageUrl": "http://issuersite.com/badge.png", + "type": "badge type", + "archived": false, + "system": { + "id": 1, + "slug": "system-slug", + "url": "http://systemsite.com", + "name": "System Name", + "email": "admin@systemsite.com", + "imageUrl": "http://systemsite.com/image.jpg", + "issuers": [ ] + }, + "criteriaUrl": "http://issuersite.com/criteria", + "criteria": [ + { + "id": 1, + "description": "criteria description", + "required": 1, + "note": "note for assessor" + }, + ... + ], + "categories": [ ], + "tags": [ ], + "milestones": [ ] } } ``` +#### Response structure + +* badge + * id + * slug + * name + * strapline + * earnerDescription + * consumerDescription + * issuerUrl + * rubricUrl + * timeValue + * timeUnits + * limit + * unique + * created + * imageUrl + * type + * archived + * [system](systems.md) `[ ]` + * id + * slug + * url + * name + * email + * imageUrl + * [issuers](issuers.md) `[ ]` + * criteriaUrl + * criteria `[ ]` + * id + * description + * required + * note + * categories `[ ]` + * tags `[ ]` + * [milestones](milestones.md) `[ ]` + ### Potential errors * **Badge not found** - ``` + ``` HTTP/1.1 404 Not Found Content-Type: application/json +``` +```json { "code": "ResourceNotFound", - "message": "Could not find badge with slug ``" + "message": "Could not find badge field: `slug`, value: " } - ``` +``` -## `Create a badge` +## Create New Badge -Creates a new badge, or updates an existing badge. +Creates a new badge in a system, issuer or program (_or updates an existing badge_). ### Expected request Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. ``` -POST /systems/:systemSlug/badges HTTP/1.1 -POST /systems/:systemSlug/issuers/:issuerSlug/badges HTTP/1.1 -POST /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug/badges -``` - -``` -HTTP/1.1 - -Content-Type: application/json - -{ - "name": "Badge Name", - "slug": "badge-slug", - "strapline": "Badge Strapline", - "earnerDescription": "Badge Description", - "consumerDescription": "Badge Description for Consumers", - "issuerUrl": "http://example.org/issuer", - "rubricUrl": "http://example.org/rubric", - "criteriaUrl": "http://example.org/criteria", - "timeValue": 10, - "timeUnits": "minutes", - "limit": 5, - "unique": false, - "imageUrl": "http://example.org/image.png", - "archived": false -} -``` - -``` -Content-Type: application/x-www-form-urlencoded - -name=Badge%20Name&slug=badge-slug&strapline=Badge%20Strapline&description=Badge%20Description&imageUrl=http%3A%2F%2Fexample.org%2Fimage.png -``` - -``` -Content-Type: multipart/form-data; boundary=… - ---… -content-disposition: form-data; name="name" - -Badge Name ---… -content-disposition: form-data; name="slug" - -badge-slug ---… -content-disposition: form-data; name="strapline" - -Badge Strapline ---… -content-disposition: form-data; name="description" - -Badge Description ---… -content-disposition: form-data; name="imageUrl" - -http://example.org/image.png ---…-- -``` - -#### Alternatively… - -Images can be uploaded and hosted by the issuer API. - -``` -Content-Type: multipart/form-data; boundary=… - ---… -… ---… -content-disposition: form-data; name="image"; filename="…" -Content-Type: image/png -Content-Transfer-Encoding: binary - -… ---…-- -``` - -``` -Content-Type: application/x-www-form-urlencoded - -…&image=data%3Aimage%2Fpng%3Bbase64%2C… -``` +POST /systems/:systemSlug/badges +POST /systems/:systemSlug/issuers/:issuerSlug/badges +POST /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug/badges +``` + +| Parameters | Required | Description | +|:-----------------------|-----------------|--------------------------| +| **slug** | _required_ | Short, computer-friendly name for the badge. Good slugs are lowercase and use dashes instead of spaces, e.g. `reading-badge`. Maximum of 50 characters and each badge must have a unique slug. +| **name** | _required_ | Name of the badge. Maximum 255 characters. +| **image** OR **imageUrl** | _required_ | Image for the program. Should be either multipart data or a URL. +| **unique** | _required_ | Boolean indicator of whether an earner can earn the badge only once. +| **criteriaUrl** | _required_ | Link to badge criteria. +| **earnerDescription** | _required_ | Description of the badge for potential earners. +| **consumerDescription** | _required_ | Description of the badge for consumers viewing it. +| **strapline** | _optional_ | Short tagline style description of the badge. Maximum 140 characters. +| **issuerUrl** | _optional_ | URL for badge issuer. +| **rubricUrl** | _optional_ | Link to any rubric material associated with the badge. +| **timeValue** | _optional_ | Badges can be associated with a time limit for earning. +| **timeUnits** | _optional_ | Time values can be expressed as `minutes`, `hours`, `days` or `weeks`. +| **limit** | _optional_ | Badges can be awarded to a fixed maximum number of earners. +| **archived** | _optional_ | Boolean indicating archived status for badge. +| **criteria** | _optional_ | Array of criteria items - each criteria should include `description` and `required` status plus optional `note` for badge reviewers. +| **type** | _required_ | Short string representing badge type. Maximum 255 characters. +| **categories** | _optional_ | Array of category names for the badge. +| **tags** | _optional_ | Array of tag names for the badge. +| **milestones** | _optional_ | Array of [milestones](milestones.md). ### Expected response ``` HTTP/1.1 201 Created Content-Type: application/json +``` +```json { "status": "created", "badge": { - "name": "Badge Name", + "id": 1, "slug": "badge-slug", - "strapline": "Badge Strapline", - "earnerDescription": "Badge Description", - "consumerDescription": "Badge Description for Consumers", - "issuerUrl": "http://example.org/issuer", - "rubricUrl": "http://example.org/rubric", - "criteriaUrl": "http://example.org/criteria", + "name": "Badge Name", + "strapline": "Badge strapline.", + "earnerDescription": "Badge description for potential earners.", + "consumerDescription": "Badge description for consumers.", + "issuerUrl": "http://issuersite.com", + "rubricUrl": "http://issuersite.com/rubric", "timeValue": 10, "timeUnits": "minutes", "limit": 5, "unique": false, - "imageUrl": "http://example.org/badge.png", - "archived": false + "created": "2014-05-21T19:22:09.000Z", + "imageUrl": "http://issuersite.com/badge.png", + "type": "badge type", + "archived": false, + "system": { + "id": 1, + "slug": "system-slug", + "url": "http://systemsite.com", + "name": "System Name", + "email": "admin@systemsite.com", + "imageUrl": "http://systemsite.com/image.jpg", + "issuers": [ ] + }, + "criteriaUrl": "http://issuersite.com/criteria", + "criteria": [ + { + "id": 1, + "description": "criteria description", + "required": 1, + "note": "note for assessor" + }, + ... + ], + "categories": [ ], + "tags": [ ], + "milestones": [ ] } } ``` +#### Response structure + +* status +* badge + * id + * slug + * name + * strapline + * earnerDescription + * consumerDescription + * issuerUrl + * rubricUrl + * timeValue + * timeUnits + * limit + * unique + * created + * imageUrl + * type + * archived + * [system](systems.md) `[ ]` + * id + * slug + * url + * name + * email + * imageUrl + * [issuers](issuers.md) `[ ]` + * criteriaUrl + * criteria `[ ]` + * id + * description + * required + * note + * categories `[ ]` + * tags `[ ]` + * [milestones](milestones.md) `[ ]` + ### Potential errors * **Invalid data** - ``` + ``` HTTP/1.1 400 Bad Request Content-Type: application/json +``` +```json { "code": "ValidationError", "message": "Could not validate required fields", @@ -245,37 +432,39 @@ Content-Type: application/json ... ] } - ``` +``` * **Duplicate entry** - ``` +``` HTTP/1.1 409 Conflict Content-Type: application/json +``` +```json { "code": "ResourceConflict", "error": "badge with that `slug` already exists", "details": { "name": "Badge Name", "slug": "badge-slug", - "strapline": "Badge Strapline", - "earnerDescription": "Badge Description", - "consumerDescription": "Badge Description for Consumers", - "issuerUrl": "http://example.org/issuer", - "rubricUrl": "http://example.org/rubric", - "criteriaUrl": "http://example.org/criteria", + "strapline": "Badge strapline.", + "earnerDescription": "Badge description for earners.", + "consumerDescription": "Badge description for consumers.", + "issuerUrl": "http://issuersite.com", + "rubricUrl": "http://issuersite.com/rubric", + "criteriaUrl": "http://issuersite.com/criteria", "timeValue": 10, "timeUnits": "minutes", "limit": 5, "unique": false, - "imageUrl": "http://example.org/badge.png", + "imageUrl": "http://issuersite.com/badge.png", "archived": false } } - ``` +``` -## `PUT /badges/` +## Update a Badge Updates an existing badge. @@ -286,102 +475,134 @@ Requests can be sent as `application/json`, `application/x-www-form-urlencoded` ``` PUT /systems/:systemSlug/badges/:badgeSlug HTTP/1.1 PUT /systems/:systemSlug/issuers/:issuerSlug/badges/:badgeSlug HTTP/1.1 -PUT /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug/badges/:badgeSlug -``` - - -``` -Content-Type: application/json - -{ - "name": "Badge Name", - "slug": "badge-slug", - "strapline": "Badge Strapline", - "earnerDescription": "Badge Description", - "consumerDescription": "Badge Description for Consumers", - "issuerUrl": "http://example.org/issuer", - "rubricUrl": "http://example.org/rubric", - "criteriaUrl": "http://example.org/criteria", - "timeValue": 10, - "timeUnits": "minutes", - "limit": 5, - "unique": false, - "imageUrl": "http://example.org/badge.png", - "archived": false -} -``` - -``` -Content-Type: application/x-www-form-urlencoded - -name=New%20Badge%20Name&new-slug=badge-slug&strapline=New%20Badge%20Strapline&description=New%20Badge%20Description&image=http%3A%2F%2Fexample.org%2Fnew-image.png -``` - -``` -Content-Type: multipart/form-data; boundary=… - ---… -content-disposition: form-data; name="name" - -New Badge Name ---… -content-disposition: form-data; name="slug" - -new-badge-slug ---… -content-disposition: form-data; name="strapline" - -New Badge Strapline ---… -content-disposition: form-data; name="description" - -New Badge Description ---… -content-disposition: form-data; name="image" - -http://example.org/new-image.png ---…-- -``` - -#### Alternatively… - -Images may be uploaded in the same manner as creating a new badge. +PUT /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug/badges/:badgeSlug HTTP/1.1 +``` + +| Parameters | Description | +|:-----------------------|--------------------------| +| **slug** | Short, computer-friendly name for the badge. Good slugs are lowercase and use dashes instead of spaces, e.g. `reading-badge`. Maximum of 50 characters and each badge must have a unique slug. +| **name** | Name of the badge. Maximum 255 characters. +| **image** OR **imageUrl** | Image for the program. Should be either multipart data or a URL. +| **unique** | Boolean indicator of whether an earner can earn the badge only once. +| **criteriaUrl** | Link to badge criteria. +| **earnerDescription** | Description of the badge for potential earners. +| **consumerDescription** | Description of the badge for consumers viewing it. +| **strapline** | Short tagline style description of the badge. Maximum 140 characters. +| **issuerUrl** | URL for badge issuer. +| **rubricUrl** | Link to any rubric material associated with the badge. +| **timeValue** | Badges can be associated with a time limit for earning. +| **timeUnits** | Time values can be expressed as `minutes`, `hours`, `days` or `weeks`. +| **limit** | Badges can be awarded to a fixed maximum number of earners. +| **archived** | Boolean indicating archived status for badge. +| **criteria** | Array of criteria items - each criteria should include `description` and `required` status plus optional `note` for badge reviewers. +| **type** | Short string representing badge type. Maximum 255 characters. +| **categories** | Array of category names for the badge. +| **tags** | Array of tag names for the badge. +| **milestones** | Array of [milestones](milestones.md). + +You only have to pass in the fields you are updating. Any fields that are not represented will be left unchanged. ### Expected response ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "status": "updated", "badge": { - "name": "Badge Name", + "id": 1, "slug": "badge-slug", - "strapline": "Badge Strapline", - "earnerDescription": "Badge Description", - "consumerDescription": "Badge Description for Consumers", - "issuerUrl": "http://example.org/issuer", - "rubricUrl": "http://example.org/rubric", - "criteriaUrl": "http://example.org/criteria", + "name": "Badge Name", + "strapline": "Badge strapline.", + "earnerDescription": "Badge description for potential earners.", + "consumerDescription": "Badge description for consumers.", + "issuerUrl": "http://issuersite.com", + "rubricUrl": "http://issuersite.com/rubric", "timeValue": 10, "timeUnits": "minutes", "limit": 5, "unique": false, - "imageUrl": "http://example.org/badge.png", - "archived": false + "created": "2014-05-21T19:22:09.000Z", + "imageUrl": "http://issuersite.com/badge.png", + "type": "badge type", + "archived": false, + "system": { + "id": 1, + "slug": "system-slug", + "url": "http://systemsite.com", + "name": "System Name", + "email": "admin@systemsite.com", + "imageUrl": "http://systemsite.com/image.jpg", + "issuers": [ ] + }, + "criteriaUrl": "http://issuersite.com/criteria", + "criteria": [ + { + "id": 1, + "description": "criteria description", + "required": 1, + "note": "note for assessor" + }, + ... + ], + "categories": [ ], + "tags": [ ], + "milestones": [ ] } } ``` +#### Response structure + +* status +* badge + * id + * slug + * name + * strapline + * earnerDescription + * consumerDescription + * issuerUrl + * rubricUrl + * timeValue + * timeUnits + * limit + * unique + * created + * imageUrl + * type + * archived + * [system](systems.md) `[ ]` + * id + * slug + * url + * name + * email + * imageUrl + * [issuers](issuers.md) `[ ]` + * criteriaUrl + * criteria `[ ]` + * id + * description + * required + * note + * categories `[ ]` + * tags `[ ]` + * [milestones](milestones.md) `[ ]` + ### Potential errors * **Invalid data** - ``` +``` HTTP/1.1 400 Bad Request Content-Type: application/json +``` +```json { "code": "ValidationError", "message": "Could not validate required fields", @@ -394,14 +615,16 @@ Content-Type: application/json ... ] } - ``` +``` * **Duplicate entry** - ``` +``` HTTP/1.1 409 Conflict Content-Type: application/json +``` +```json { "code": "ResourceConflict", "error": "badge with that `slug` already exists", @@ -422,7 +645,21 @@ Content-Type: application/json "archived": false } } - ``` +``` + +* **Badge not found** + +``` + HTTP/1.1 404 Not Found + Content-Type: application/json +``` + +```json + { + "code": "ResourceNotFound", + "message": "Could not find badge field: `slug`, value: " + } +``` ## Delete a Badge @@ -433,7 +670,7 @@ Deletes an existing badge. ``` DELETE /systems/:systemSlug/badges/:badgeSlug HTTP/1.1 DELETE /systems/:systemSlug/issuers/:issuerSlug/badges/:badgeSlug HTTP/1.1 -DELETE /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug/badges/:badgeSlug +DELETE /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug/badges/:badgeSlug HTTP/1.1 ``` ### Expected response @@ -441,24 +678,33 @@ DELETE /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug/badges/:ba ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "status": "deleted", "badge": { - "name": "Badge Name", + "id": 1, "slug": "badge-slug", - "strapline": "Badge Strapline", - "earnerDescription": "Badge Description", - "consumerDescription": "Badge Description for Consumers", - "issuerUrl": "http://example.org/issuer", - "rubricUrl": "http://example.org/rubric", - "criteriaUrl": "http://example.org/criteria", + "name": "Badge Name", + "strapline": "Badge strapline.", + "earnerDescription": "Badge description for potential earners.", + "consumerDescription": "Badge description for consumers.", + "issuerUrl": "http://issuersite.com", + "rubricUrl": "http://issuersite.com/rubric", "timeValue": 10, "timeUnits": "minutes", "limit": 5, "unique": false, - "imageUrl": "http://example.org/badge.png", - "archived": false + "created": "2014-05-21T19:22:09.000Z", + "imageUrl": "http://issuersite.com/badge.png", + "type": "badge type", + "archived": false, + "criteriaUrl": "http://issuersite.com/criteria", + "criteria": [ ], + "categories": [ ], + "tags": [ ], + "milestones": [ ] } } ``` @@ -467,12 +713,14 @@ Content-Type: application/json * **Badge not found** - ``` +``` HTTP/1.1 404 Not Found Content-Type: application/json +``` +```json { "code": "ResourceNotFound", - "message": "Could not find badge with slug ``" + "message": "Could not find badge field: `slug`, value: " } - ``` +``` diff --git a/docs/claim-codes.md b/docs/claim-codes.md new file mode 100644 index 0000000..c8bb27c --- /dev/null +++ b/docs/claim-codes.md @@ -0,0 +1,536 @@ +# Claim Codes + +Earners can use claim codes to claim badges. For example, a claim code can be distributed at an event or given to the earner on completion of an achievement. The issuer site can facilitate issuing badges by allowing earners to submit claim codes. + +| NAME | VALUE | +|:---|:---| +| `id` | __integer__ - _id from database entry_ | +| `code` | __string__ - _code used to claim a badge_ | +| `claimed` | __boolean__ | +| `email` | __string__ | +| `multiuse` | __boolean__ - _claim codes can be single use or multi-use_ | +| `badge` | [badge](badges.md) _claim code is for_ | + +## Endpoints + +* [Retrieve Claim Codes](#retrieve-claim-codes) + * `GET /systems/:slug/badges/:slug/codes` + * `GET /systems/:slug/issuers/:slug/badges/:slug/codes` + * `GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/codes` +* [Retrieve Specific Claim Code](#retrieve-specific-claim-code) + * `GET /systems/:slug/badges/:slug/codes/:code` + * `GET /systems/:slug/issuers/:slug/badges/:slug/codes/:code` + * `GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/codes/:code` +* [Retrieve Badge from Claim Code](#retrieve-badge-from-claim-code) + * `GET /systems/:slug/codes/:code` + * `GET /systems/:slug/issuers/:slug/codes/:code` + * `GET /systems/:slug/issuers/:slug/programs/:slug/codes/:code` +* [Create Claim Code](#create-claim-code) + * `POST /systems/:slug/badges/:slug/codes` + * `POST /systems/:slug/issuers/:slug/badges/:slug/codes` + * `POST /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/codes` +* [Create Random Code](#create-random-code) + * `POST /systems/:slug/badges/:slug/codes/random` + * `POST /systems/:slug/issuers/:slug/badges/:slug/codes/random` + * `POST /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/codes/random` +* [Delete Claim Code](#delete-claim-code) + * `DELETE /systems/:slug/badges/:slug/codes/:code` + * `DELETE /systems/:slug/issuers/:slug/badges/:slug/codes/:code` + * `DELETE /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/codes/:code` +* [Claim a Code](#claim-a-code) + * `POST /systems/:slug/badges/:slug/codes/:code/claim` + * `POST /systems/:slug/issuers/:slug/badges/:slug/codes/:code/claim` + * `POST /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/codes/:code/claim` + +## Retrieve Claim Codes + +Retrieves all claim codes for a badge within a system, issuer or program. + +### Expected request + +``` +GET /systems/:slug/badges/:slug/codes +GET /systems/:slug/issuers/:slug/badges/:slug/codes +GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/codes +``` + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "claimCodes": [ + { + "id": 1, + "code": "0fba9c4457", + "claimed": false, + "email": "someone@somewhere.com", + "multiuse": false + }, + ... + ], + "badge": { + ... + } +} +``` + +#### Response structure + +* claimCodes `[ ]` + * id + * code + * claimed + * email + * multiuse +* [badge](badges.md) + +### Potential errors + +*None* + +## Retrieve Specific Claim Code + +Retrieve the details for a specific claim code for a badge. + +### Expected request + +``` +GET /systems/:slug/badges/:slug/codes/:code +GET /systems/:slug/issuers/:slug/badges/:slug/codes/:code +GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/codes/:code +``` + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "badge": { + ... + }, + "claimCode": { + "id": 1, + "code": "2f91d622dd", + "claimed": false, + "email": null, + "multiuse": false + } +} +``` + +#### Response structure + +* [badge](badges.md) +* claimCode + * id + * code + * claimed + * email + * multiuse + +### Potential errors + +* **Claim code not found** + +``` +HTTP/1.1 404 Not Found +Content-Type: application/json +``` + +```json +{ + "code": "ResourceNotFound", + "message": "Could not find the request claim code: " +} +``` + +## Retrieve Badge from Claim Code + +Retrieve the details for a badge using a claim code within a system, issuer or program context. + +### Expected request + +``` +GET /systems/:slug/codes/:code +GET /systems/:slug/issuers/:slug/codes/:code +GET /systems/:slug/issuers/:slug/programs/:slug/codes/:code +``` + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "badge": { + "id": 1, + "slug": "badge-slug", + "name": "Badge Name", + "strapline": "Badge strapline.", + "earnerDescription": "Description for earners.", + "consumerDescription": "Description for consumers.", + "issuerUrl": "http://issuersite.com", + "rubricUrl": "http://issuersite.com/rubric", + "timeValue": 0, + "timeUnits": "minutes", + "limit": 0, + "unique": 0, + "created": "2014-05-21T19:22:09.000Z", + "imageUrl": "http://issuersite.com/badeg.jpg", + "type": "badge type", + "archived": false, + "system": { + ... + }, + "criteriaUrl": "http://issuersite.com/criteria", + "criteria": [ ], + "categories": [ ], + "tags": [ ], + "milestones": [ ], + "claimed": 0 + } +} +``` + +#### Response structure + +* [badge](badges.md) + * ... + * claimed + +### Potential errors + +* **Claim code not found** + +``` +HTTP/1.1 404 Not Found +Content-Type: application/json +``` + +```json +{ + "code": "ResourceNotFound", + "message": "Could not find the request claim code: " +} +``` + +## Create Claim Code + +Create a claim code for a badge. + +### Expected request + +``` +POST /systems/:slug/badges/:slug/codes +POST /systems/:slug/issuers/:slug/badges/:slug/codes +POST /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/codes +``` + +Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. + +| Parameters | Required | Description | +|:-----------------------|-----------------|--------------------------| +| **code** | required | The claim code you are creating. String with maximum length 255 characters. | +| **claimed** | optional | Boolean indicator of whether the badge has been claimed. +| **multiuse** | optional | Boolean indicator of whether the badge is multiuse or not (single use). +| **email** | optional | + +### Expected response + +``` +HTTP/1.1 201 Created +Content-Type: application/json +``` + +```json +{ + "status": "created", + "claimCode": { + "id": 1, + "code": "abcde12345", + "claimed": false, + "multiuse": false + }, + "badge": { + ... + } +} +``` + +#### Response structure + +* status +* claimCode + * id + * code + * claimed + * multiuse +* [badge](badges.md) + +#### Potential errors + +* **Invalid data** + +``` + HTTP/1.1 400 Bad Request + Content-Type: application/json +``` + +```json + { + "code": "ValidationError", + "message": "Could not validate required fields", + "details": [ + { + "message": "String is not in range", + "field": "code", + "value": "..." + }, + ... + ] + } +``` + +* **Duplicate entry** + +``` + HTTP/1.1 409 Conflict + Content-Type: application/json +``` + +```json + { + "code": "ResourceConflict", + "error": "claimCode with that `code` already exists", + "details": { + ... + } + } +``` + +## Create Random Code + +Creates a random claim code - BadgeKit API will generate the claim code using a random algorithm. + +### Expected request + +``` +POST /systems/:slug/badges/:slug/codes/random +POST /systems/:slug/issuers/:slug/badges/:slug/codes/random +POST /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/codes/random +``` + +Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. + +| Parameters | Required | Description | +|:-----------------------|-----------------|--------------------------| +| **claimed** | optional | Boolean indicator of whether the badge has been claimed. +| **multiuse** | optional | Boolean indicator of whether the badge is multiuse or not (single use). +| **email** | optional | + +### Expected response + +``` +HTTP/1.1 201 Created +Content-Type: application/json +``` + +```json +{ + "status": "created", + "claimCode": { + "id": 1, + "code": "abcde12345", + "claimed": false, + "multiuse": false + }, + "badge": { + ... + } +} +``` + +#### Response structure + +* status +* claimCode + * id + * code + * claimed + * multiuse +* [badge](badges.md) + +### Potential errors + +*None* + +## Delete Claim Code + +Delete a claim code. + +### Expected request + +``` +DELETE /systems/:systemSlug/badges/:badgeSlug/codes/:code +DELETE /systems/:systemSlug/issuers/:issuerSlug/badges/:badgeSlug/codes/:code +DELETE /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug/badges/:badgeSlug/codes/:code +``` + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "status": "deleted", + "claimCode": { + "id": 1, + "code": "0fba9c4457", + "claimed": false, + "email": null, + "multiuse": false + }, + "badge": { + "id": 1, + "slug": "badge-slug", + "name": "Badge Name", + "strapline": "Badge strapline.", + "earnerDescription": "Badge description for potential earners.", + "consumerDescription": "Badge description for consumers.", + "issuerUrl": "http://issuersite.com", + "rubricUrl": "http://issuersite.com/rubric", + "timeValue": 10, + "timeUnits": "minutes", + "limit": 5, + "unique": false, + "created": "2014-05-21T19:22:09.000Z", + "imageUrl": "http://issuersite.com/badge.png", + "type": "badge type", + "archived": false, + "criteriaUrl": "http://issuersite.com/criteria", + "criteria": [ ], + "categories": [ ], + "tags": [ ], + "milestones": [ ] + } +} +``` + +#### Response structure + +* status +* claimCode +* [badge](badges.md) + +### Potential errors + +* **Claim code not found** + +``` + HTTP/1.1 404 Not Found + Content-Type: application/json +``` + +```json + { + "code": "ResourceNotFound", + "message": "Could not find claimCode field: `code`, value: " + } +``` + +## Claim a Code + +Claim a code. + +### Expected request + +``` +POST /systems/:slug/badges/:slug/codes/:code/claim +POST /systems/:slug/issuers/:slug/badges/:slug/codes/:code/claim +POST /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/codes/:code/claim +``` + +Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. + +| Parameters | Required | +|:-----------------------|-----------------| +| **email** | optional | + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "status": "updated", + "claimCode": { + "id": 1, + "code": "abcde12345", + "claimed": false, + "email": null, + "multiuse": false + }, + "badge": { + ... + } +} +``` + +#### Response structure + +* status +* claimCode + * id + * code + * claimed + * email + * multiuse +* [badge](badges.md) + +### Potential errors + +* **Claim code already claimed (if single use)** + +``` +HTTP/1.1 400 Bad Request +Content-Type: application/json +``` + +```json +{ + "code": "CodeAlreadyUsed", + "message": "Claim code `code` has already been claimed" +} +``` + +* **Claim code not found** + +``` +HTTP/1.1 404 Not Found +Content-Type: application/json +``` + +```json +{ + "code": "ResourceNotFound", + "message": "Could not find claimCode field: `code`, value: " +} +``` diff --git a/docs/issuers.md b/docs/issuers.md index 4835c1c..36dfd01 100644 --- a/docs/issuers.md +++ b/docs/issuers.md @@ -1,13 +1,34 @@ # Issuers -## Retrieve list of issuers +Issuers represent mid-level admin in BadgeKit. Each issuer belongs to a single [system](systems.md), optionally along with other issuers. An issuer can contain one or more [programs](programs.md). Badges can be associated with an issuer, which will typically be a single organization within a badging system, such as a library, museum or school. -Retrieves all available issuers, filtered by system. +| NAME | VALUE | +|:---|:---| +| `id` | integer - _ID from database entry._ | +| `slug` | string - _Short, computer-friendly name for the issuer. Used to identify issuer in API endpoints._ | +| `url` | string - _Issuer URL._ | +| `name` | string - _Name of the issuer._ | +| `description` | string - _A short, human-friendly description of the issuer._ | +| `email` | string - _Email address associated with the badge administrator of the issuer._ | +| `imageUrl` | string - _Image for the issuer._ | +| `programs` | array - _[Programs](programs.md) in the issuer._ | + +## Endpoints + +* [`GET /systems//issuers`](#get-systemsslugissuers) +* [`GET /systems//issuers/`](#get-systemsslugissuersslug) +* [`POST /systems//issuers`](#post-systemsslugissuers) +* [`PUT /systems//issuers/`](#put-systemsslugissuersslug) +* [`DELETE /systems//issuers/`](#delete-systemsslugissuersslug) + +## `GET /systems//issuers` + +Retrieves all available issuers in the specified system. ### Expected request ``` -GET /systems/:systemSlug/issuers HTTP/1.1 +GET /systems/:systemSlug/issuers ``` ### Expected response @@ -15,31 +36,61 @@ GET /systems/:systemSlug/issuers HTTP/1.1 ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "issuers": [ { - "name": "Issuer Name", + "id": 1, "slug": "issuer-slug", - "description": "Issuer description" + "url": "http://issuersite.com", + "name": "Issuer Name", + "description": "Issuer description.", + "email": "admin@issuersite.com", + "imageUrl": "http://issuersite.com/image.jpg", + "programs": [ + { + "id": 1, + "slug": "program-slug", + "url": "http://programsite.com", + "name": "Program Name", + "description": "Program description.", + "email": "admin@programsite.com", + "imageUrl": "http://programsite.com/image.jpg" + }, + ... + ] }, ... ] } ``` +#### Response structure + +* issuers `[ ]` + * id + * slug + * url + * name + * description + * email + * imageUrl + * [programs](programs.md) `[ ]` + ### Potential errors *None* -## Retrieve a specific issuer +## `GET /systems//issuers/` -Retrieves a specific issuer. +Retrieves a specific issuer using its slug. ### Expected request ``` -GET /systems/:systemSlug/issuers/:issuerSlug HTTP/1.1 +GET /systems/:systemSlug/issuers/:issuerSlug ``` ### Expected response @@ -47,43 +98,74 @@ GET /systems/:systemSlug/issuers/:issuerSlug HTTP/1.1 ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "issuer": { - "name": "Issuer Name", + "id": 1, "slug": "issuer-slug", - "description": "Issuer Description" + "url": "http://issuersite.com", + "name": "Issuer Name", + "description": "Issuer description.", + "email": "admin@issuersite.com", + "imageUrl": "http://issuersite.com/image.jpg", + "programs": [ + { + "id": 1, + "slug": "program-slug", + "url": "http://programsite.com", + "name": "Program Name", + "description": "Program description.", + "email": "admin@programsite.com", + "imageUrl": "http://programsite.com/image.jpg" + }, + ... + ] } } ``` +#### Response structure + +* issuer + * id + * slug + * url + * name + * description + * email + * imageUrl + * [programs](programs.md) `[ ]` + ### Potential errors * **Issuer not found** - ``` +``` HTTP/1.1 404 Not Found Content-Type: application/json +``` +```json { "code": "ResourceNotFound", - "message": "Could not find issuer with slug ``" + "message": "Could not find issuer field: `slug`, value " } - ``` +``` -## Create a new issuer +## `POST /systems//issuers` Creates a new issuer. ### Expected request -`/systems/:systemSlug/issuers/:issuerSlug` +``` +POST /systems/:systemSlug/issuers +``` Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. - - - | Parameters | Required | Description | |:-----------------------|-----------------|-------------------------| | **slug** | required | Short, computer-friendly name for the issuer. Good slugs are lowercase and use dashes instead of spaces, e.g. `chicago-public-library`. Maximum of 50 characters and each issuer must have a unique slug. @@ -98,27 +180,58 @@ Requests can be sent as `application/json`, `application/x-www-form-urlencoded` ``` HTTP/1.1 201 Created Content-Type: application/json +``` +```json { "status": "created", "issuer": { + "id": 1, "slug": "issuer-slug", + "url": "http://issuersite.com", "name": "Issuer Name", - "url": "https://example.org/issuer/", - "email": "issuer-badges@example.org", - "description": "Issuer Description" + "description": "Issuer description.", + "email": "admin@issuersite.com", + "imageUrl": "http://issuersite.com/image.jpg", + "programs": [ + { + "id": 1, + "slug": "program-slug", + "url": "http://programsite.com", + "name": "Program Name", + "description": "Program description.", + "email": "admin@programsite.com", + "imageUrl": "http://programsite.com/image.jpg" + }, + ... + ] } } ``` +#### Response structure + +* status +* issuer + * id + * slug + * url + * name + * description + * email + * imageUrl + * [programs](programs.md) `[ ]` + ### Potential errors * **Invalid data** - ``` +``` HTTP/1.1 400 Bad Request Content-Type: application/json +``` +```json { "code": "ValidationError", "message": "Could not validate required fields", @@ -131,65 +244,98 @@ Content-Type: application/json ... ] } - ``` +``` * **Duplicate entry** - ``` +``` HTTP/1.1 409 Conflict Content-Type: application/json +``` +```json { "code": "ResourceConflict", - "error": issuer with that `slug` already exists", + "error": "issuer with that `slug` already exists", "details": { "slug": "issuer-slug", "name": "Issuer Name", - "url": "https://example.org/issuer/", - "email": "issuer-badges@example.org", - "description": "Issuer Description" + "url": "http://issuersite.com", + "email": "admin@issuersite.com", + "description": "Issuer description." } } - ``` +``` -## Update an issuer +## `PUT /systems//issuers/` Updates an existing issuer. ### Expected request -`PUT /systems/:systemSlug/issuers/:issuerSlug` +``` +PUT /systems/:systemSlug/issuers/:issuerSlug +``` Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. -See above for parameters. You only have to pass in the fields you are updating. Any fields that are not represented will be left unchanged. +| Parameters | Description | +|:-----------------------|--------------------------| +| **slug** | Short, computer-friendly name for the issuer. Good slugs are lowercase and use dashes instead of spaces, e.g. `chicago-public-library`. Maximum of 50 characters and each issuer must have a unique slug. +| **name** | Name of the issuer. Maximum of 255 characters. +| **url** | URL for the issuer. Must be fully qualified, e.g. https://www.example.org, **NOT** just www.example.org. +| **description** | A short, human-friendly description of the issuer. Maximum of 255 characters +| **email** | Email address associated with the badge administrator of the issuer. +| **image** | Image for the issuer. Should be either multipart data or a URL. + +You only have to pass in the fields you are updating. Any fields that are not represented will be left unchanged. ### Expected response ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "status": "updated", "issuer": { + "id": 1, "slug": "issuer-slug", - "name": Updated Issuer Name", - "url": "https://example.org/issuer/", - "email": "issuer-badges@example.org", - "description": "Updated Issuer Description" + "url": "http://issuersite.com", + "name": "Updated Issuer Name", + "description": "Updated Issuer Description", + "email": "admin@issuersite.com", + "imageUrl": "http://issuersite.com/image.jpg", + "programs": [ ] } } ``` +#### Response structure + +* status +* issuer + * id + * slug + * url + * name + * description + * email + * imageUrl + * [programs](programs.md) `[ ]` + ### Potential errors * **Invalid data** - ``` +``` HTTP/1.1 400 Bad Request Content-Type: application/json +``` +```json { "code": "ValidationError", "message": "Could not validate required fields", @@ -206,31 +352,33 @@ Content-Type: application/json * **Duplicate entry** - ``` +``` HTTP/1.1 409 Conflict Content-Type: application/json +``` +```json { "code": "ResourceConflict", "error": "issuer with that `slug` already exists", "details": { "slug": "issuer-slug", "name": "Issuer Name", - "url": "https://example.org/issuer/", - "email": "issuer-badges@example.org", - "description": "Issuer Description" + "url": "http://issuersite.com", + "email": "admin@issuersite.com", + "description": "Issuer description." } } ``` -## Delete an existing issuer +## `DELETE /systems//issuers/` Deletes an existing issuer. ### Expected request ``` -DELETE /system/:systemSlug/issuers/:issuerSlug HTTP/1.1 +DELETE /systems/:systemSlug/issuers/:issuerSlug ``` ### Expected response @@ -238,15 +386,20 @@ DELETE /system/:systemSlug/issuers/:issuerSlug HTTP/1.1 ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "status": "deleted", "issuer": { + "id": 1, "slug": "issuer-slug", + "url": "http://issuersite.com", "name": "Issuer Name", - "url": "https://example.org/issuer/", - "email": "issuer-badges@example.org", - "description": "Issuer Description" + "description": "Issuer description.", + "email": "admin@issuersite.com", + "imageUrl": "http://issuersite.com/image.jpg", + "programs": [ ] } } ``` @@ -255,12 +408,14 @@ Content-Type: application/json * **Issuer not found** - ``` +``` HTTP/1.1 404 Not Found Content-Type: application/json +``` +```json { "code": "ResourceNotFound", - "message": "Could not find issuer with slug ``" + "message": "Could not find issuer field: `slug`, value: " } - ``` +``` diff --git a/docs/issuing.md b/docs/issuing.md new file mode 100644 index 0000000..223332b --- /dev/null +++ b/docs/issuing.md @@ -0,0 +1,325 @@ +# Issuing + +Issuing is the process of awarding a badge to a specific earner. In BadgeKit API, issuing a badge means creating a badge instance. __Note that when a review is submitted in which an earner application for a badge is approved, this does not mean that the badge is automatically issued. Issuer sites must use the below API endpoints to issue badges, marking any relevant applications as processed when this occurs. These endpoints also apply to badges issued without the assessment process, for example badges issued directly to earner email addresses or using claim codes.__ + +## Badge Instances + +| NAME | VALUE | +|:---|:---| +| `slug` | __string__ | +| `email` | __email address__ - _earner email_ | +| `expires` | __timestamp__ - _optional expiry date_ | +| `issuedOn` | __timestamp__ - _API will generate_ | +| `claimCode` | [claim code](claim-codes.md) - _if badge is issued using claim code_ | +| `assertionUrl` | __url__ - _location of issued badge assertion, generated by API_ | +| `badge` | [badge](badges.md) - _API endpoints return badge issued along with instances_ | + +## Endpoints + +* [Retrieve Badge Instances](#retrieve-badge-instances) + * `GET /systems/:slug/badges/:slug/instances` + * `GET /systems/:slug/issuers/:slug/badges/:slug/instances` + * `GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/instances` +* [Retrieve a Specific Instance](#retrieve-a-specific-instance) + * `GET /systems/:slug/instances/:email` + * `GET /systems/:slug/issuers/:slug/instances/:email` + * `GET /systems/:slug/issuers/:slug/programs/:slug/instances/:email` + * `GET /systems/:slug/badges/:slug/instances/:email` + * `GET /systems/:slug/issuers/:slug/badges/:slug/instances/:email` + * `GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/instances/:email` +* [Create a Badge Instance](#create-a-badge-instance) + * `POST /systems/:slug/badges/:slug/instances` + * `POST /systems/:slug/issuers/:slug/badges/:slug/instances` + * `POST /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/instances` +* [Delete an Instance](#delete-an-instance) + * `DELETE /systems/:slug/badges/:slug/instances/:email` + * `DELETE /systems/:slug/issuers/:slug/badges/:slug/instances/:email` + * `DELETE /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/instances/:email` + +## Retrieve Badge Instances + +Retrieves all instances for a specific badge within a [system](systems.md), [issuer](issuers.md) or [program](programs.md). + +### Expected request + +``` +GET /systems/:slug/badges/:slug/instances +GET /systems/:slug/issuers/:slug/badges/:slug/instances +GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/instances +``` + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "instances": [ + { + "slug": "instance-slug", + "email": "earneremail@adomain.com", + "expires": "2014-10-29T10:16:01.000Z", + "issuedOn": "2014-05-29T10:16:01.000Z", + "claimCode": "claim-code", + "assertionURl": "http://issuersite.com/public/assertions/instance-slug", + "badge": { + "id": 1, + "slug": "badge-slug", + ... + } + }, + ... + ] +} +``` + +#### Response structure + +* instances `[ ]` + * slug + * email + * expires + * issuedOn + * claimCode + * assertionUrl + * [badge](badges.md) + +### Potential errors + +*None* + +## Retrieve a Specific Instance + +Retrieve a specific instance of a badge. + +### Expected request + +``` +GET /systems/:slug/instances/:email +GET /systems/:slug/issuers/:slug/instances/:email +GET /systems/:slug/issuers/:slug/programs/:slug/instances/:email +GET /systems/:slug/badges/:slug/instances/:email +GET /systems/:slug/issuers/:slug/badges/:slug/instances/:email +GET /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/instances/:email +``` + +You can retrieve instances of badges awarded to a particular email address, for systems, issuers, programs and badges. + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "instances": [ + { + "slug": "instance-slug", + "email": "earneremail@adomain.com", + "expires": "2014-10-29T10:16:01.000Z", + "issuedOn": "2014-05-29T10:16:01.000Z", + "claimCode": "claim-code", + "assertionURl": "http://issuersite.com/public/assertions/instance-slug", + "badge": { + "id": 1, + "slug": "badge-slug", + ... + } + }, + ... + ] +} +``` + +#### Response structure + +* instances `[ ]` + * slug + * email + * expires + * issuedOn + * claimCode + * assertionUrl + * [badge](badges.md) + +### Potential errors + +* **Instance not found** + +``` +HTTP/1.1 404 Not Found +Content-Type: application/json +``` + +```json +{ + "code": "ResourceNotFound", + "message": "Could not find badgeInstance field: `email`, value: " +} +``` + +## Create a Badge Instance + +To actually issue (or "award") a badge to an earner in BadgeKit API, you create a badge instance. A badge instance is an awarded badge, issued to a specific earner. + +### Expected request + +``` +POST /systems/:slug/badges/:slug/instances +POST /systems/:slug/issuers/:slug/badges/:slug/instances +POST /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/instances +``` + +Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. + +| Parameters | Required | Description | +|:-----------------------|-----------------|--------------------------| +| **email** | required | The email address for the earner the badge is being issued to. | +| **claimCode** | optional | A claim code for the badge. | +| **slug** | optional | Instance slug. (API will generate if not provided.) | +| **issuedOn** | optional | Timestamp representing date issued. (API will generate if not provided.) | +| **expires** | optional | Timestamp representing date instance expires. | + +### Expected response + +``` +HTTP/1.1 201 Created +Content-Type: application/json +``` + +```json +{ + "status": "created", + "instance": { + "slug": "abcdefghi123456789abcdefghi123456789", + "email": "earner@somedomain.com", + "expires": null, + "issuedOn": "2014-05-29T10:16:01.654Z", + "claimCode": "abcde12345", + "assertionUrl": "http://issuersite.com/public/assertions/abcdefghi123456789abcdefghi123456789", + "badge": null + } +} +``` + +The assertion URL represents the location of the badge assertion. A badge assertion contains the details of an issued badge. See the latest [assertion](https://github.com/mozilla/openbadges-specification/blob/master/Assertion/latest.md) specification for more information. Claim code is only returned if the badge instance was created using a claim code. + +#### Response structure + +* status +* instance + * slug + * email + * expires + * issuedOn + * claimCode + * assertionUrl + * [badge](badges.md) + +#### Potential errors + +* **Invalid data** + +``` + HTTP/1.1 400 Bad Request + Content-Type: application/json +``` + +```json + { + "code": "ValidationError", + "message": "Could not validate required fields", + "details": [ + { + "field": "email", + "value": "..." + }, + ... + ] + } +``` + +* **Duplicate entry** + +``` + HTTP/1.1 409 Conflict + Content-Type: application/json +``` + +```json + { + "code": "ResourceConflict", + "message": "User has already been awarded badge ", + "details": { + "assertionUrl": "http://issuersite.com/public/assertions/abcde..." + ... + } + } +``` + +## Delete an Instance + +Delete a badge instance within a system, issuer or program context. + +### Expected request + +``` +DELETE /systems/:slug/badges/:slug/instances/:email +DELETE /systems/:slug/issuers/:slug/badges/:slug/instances/:email +DELETE /systems/:slug/issuers/:slug/programs/:slug/badges/:slug/instances/:email +``` + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "instance": { + "slug": "abcdefghi123456789abcdefghi123456789", + "email": "earner@somedomain.com", + "expires": null, + "issuedOn": "2014-05-29T10:16:01.654Z", + "claimCode": "abcde12345", + "assertionUrl": "http://issuersite.com/public/assertions/abcdefghi123456789abcdefghi123456789", + "badge": { + ... + } + } +} +``` + +#### Response structure + +* instance + * slug + * email + * expires + * issuedOn + * claimCode + * assertionUrl + * [badge](badges.md) + +#### Potential errors + +* **Instance not found** + +``` +HTTP/1.1 404 Not Found +Content-Type: application/json +``` + +```json +{ + "code": "ResourceNotFound", + "message": "Could not find badgeInstance field: `email`, value: " +} +``` diff --git a/docs/milestones.md b/docs/milestones.md new file mode 100644 index 0000000..97ea791 --- /dev/null +++ b/docs/milestones.md @@ -0,0 +1,479 @@ +# Milestones + +Milestones give issuers the ability to award badges in recognition that the earner has earned a set of other badges. A milestone badge therefore represents a higher-level achievement, with the contributing badges representing more granular badges, culminating in the milestone. A milestone badge can be defined as available to earners of a specific set of other badges. + +| NAME | VALUE | +|:---|:---| +| `id` | __integer__ - _id from database_ | +| `action` | __string__ - _can be `issue` or `queue-application`_ | +| `numberRequired` | __integer__ - _number of support badges required to earn the milestone badge_ | +| `primaryBadge` | __[badge](badges.md)__ - _the milestone badge itself_ | +| `supportBadges` | __array__ - _support [badges](badges.md) required to earn the milestone_ | + +## Endpoints +* [`GET /systems/:slug/milestones`](#get-systemsslugmilestones) +* [`GET /systems/:slug/milestones/:milestoneId`](#get-systemsslugmilestonesmilestoneid) +* [`POST /systems/:slug/milestones`](#post-systemsslugmilestones) +* [`PUT /systems/:slug/milestones/:milestoneId`](#put-systemsslugmilestonesmilestoneid) +* [`DELETE /systems/:slug/milestones/:milestoneId`](#delete-systemsslugmilestonesmilestoneid) + +## `GET /systems/:slug/milestones` + +Retrieve milestones within a system. + +### Expected request + +``` +GET /systems//milestones +``` + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "milestones": [ + { + "id": 1, + "action": "issue", + "numberRequired": 3, + "primaryBadge": { + "id": 4, + "slug": "milestone-badge-slug", + "name": "Milestone Badge Name", + "strapline": "Milestone strapline.", + "earnerDescription": "Milestone earner description.", + "consumerDescription": "Milesotne consumer description.", + "issuerUrl": "http://issuersite.com", + "rubricUrl": "http://issuersite.com/rubric", + "timeValue": 0, + "timeUnits": "minutes", + "limit": 0, + "unique": 0, + "created": "2014-05-29T21:24:32.000Z", + "type": "milestone badge type", + "archived": false, + "criteriaUrl": "http://issuersite.com/milestone-badge-slug/criteria", + "criteria": [ ], + "categories": [ ], + "tags": [ ], + "milestones": [ ] + }, + "supportBadges": [ + { + "id": 1, + "slug": "support-badge-slug", + ... + }, + ... + ] + } + ] +} +``` + +#### Response structure + +* milestones `[ ]` + * id + * action + * numberRequired + * primaryBadge ([badge](badges.md)) + * id + * slug + * strapline + * earnerDescription + * consumerDescription + * issuerUrl + * rubricUrl + * timeValue + * timeUnits + * limit + * unique + * created + * type + * archived + * criteriaUrl + * criteria `[ ]` + * categories `[ ]` + * tags `[ ]` + * milestones `[ ]` + * supportBadges `[ ]` + * id + * slug + * ... + +### Potential errors + +*None* + +## `GET /systems/:slug/milestones/:milestoneId` + +Retrieve a specific milestone using its ID. + +### Expected request + +``` +GET /systems//milestones/ +``` + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "milestone": + { + "id": 1, + "action": "issue", + "numberRequired": 3, + "primaryBadge": { + "id": 4, + "slug": "milestone-badge-slug", + "name": "Milestone Badge Name", + "strapline": "Milestone strapline.", + "earnerDescription": "Milestone earner description.", + "consumerDescription": "Milesotne consumer description.", + "issuerUrl": "http://issuersite.com", + "rubricUrl": "http://issuersite.com/rubric", + "timeValue": 0, + "timeUnits": "minutes", + "limit": 0, + "unique": 0, + "created": "2014-05-29T21:24:32.000Z", + "type": "milestone badge type", + "archived": false, + "criteriaUrl": "http://issuersite.com/milestone-badge-slug/criteria", + "criteria": [ ], + "categories": [ ], + "tags": [ ], + "milestones": [ ] + }, + "supportBadges": [ + { + "id": 1, + "slug": "support-badge-slug", + ... + }, + ... + ] + } +} +``` + +#### Response structure + +* milestone + * id + * action + * numberRequired + * primaryBadge ([badge](badges.md)) + * id + * slug + * strapline + * earnerDescription + * consumerDescription + * issuerUrl + * rubricUrl + * timeValue + * timeUnits + * limit + * unique + * created + * type + * archived + * criteriaUrl + * criteria `[ ]` + * categories `[ ]` + * tags `[ ]` + * milestones `[ ]` + * supportBadges `[ ]` + * id + * slug + * ... + +### Potential errors + +* **Milestone not found** + +``` +HTTP/1.1 404 Not Found +Content-Type: application/json +``` + +```json +{ + "code": "NotFoundError", + "message": "Could not find milestone with `id` " +} +``` + +## `POST /systems/:slug/milestones` + +Create a new milestone. + +### Expected request + +``` +POST /systems//milestones +``` + +Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. + +| Parameters | Required | Description | +|:-----------------------|-----------------|--------------------------| +| **numberRequired** | required | Integer representing how many support badges are required to earn the milestone badge. | +| **primaryBadgeId** | required | Id of primary badge, which is the milestone badge itself. | +| **action** | optional | Can be `issue` or `queue-application`. | +| **supportBadges** | required | Array containing ids of support badges. | + +### Expected response + +``` +HTTP/1.1 201 Created +Content-Type: application/json +``` + +```json +{ + "status": "created", + "milestone": { + "id": 1, + "action": "issue", + "numberRequired": 3, + "primaryBadge": { + "id": 4, + "slug": "milestone-badge-slug", + "name": "Milestone Badge Name", + "strapline": "Milestone strapline.", + "earnerDescription": "Milestone earner description.", + "consumerDescription": "Milesotne consumer description.", + "issuerUrl": "http://issuersite.com", + "rubricUrl": "http://issuersite.com/rubric", + "timeValue": 0, + "timeUnits": "minutes", + "limit": 0, + "unique": 0, + "created": "2014-05-29T21:24:32.000Z", + "type": "milestone badge type", + "archived": false, + "criteriaUrl": "http://issuersite.com/milestone-badge-slug/criteria", + "criteria": [ ], + "categories": [ ], + "tags": [ ], + "milestones": [ ] + }, + "supportBadges": [ + { + "id": 1, + "slug": "support-badge-slug", + ... + }, + ... + ] + } +} +``` + +#### Response structure + +* status +* milestone + * id + * action + * numberRequired + * primaryBadge ([badge](badges.md)) + * id + * slug + * ... + * supportBadges `[ ]` + * id + * slug + * ... + +#### Potential Errors + +* **Invalid data** + +``` + HTTP/1.1 400 Bad Request + Content-Type: application/json +``` + +```json + { + "code": "ValidationError", + "message": "Could not validate required fields", + "details": [ + { + "field": "id", + "value": "..." + }, + ... + ] + } +``` + +## `PUT /systems/:slug/milestones/:milestoneId` + +Update an existing milestone. + +### Expected request + +``` +PUT /systems//milestones/ +``` + +Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. + +| Parameters | Description | +|:-----------------------|--------------------------| +| **numberRequired** | Integer representing how many support badges are required to earn the milestone badge. | +| **primaryBadgeId** | Id of primary badge which is the milestone badge. | +| **action** | Can be `issue` or `queue-application`. | +| **supportBadges** | Array containing ids of support badges. | + +You only have to pass in the fields you are updating. Any fields that are not represented will be left unchanged. + +### Expected response + +``` +HTTP/1.1 200 OK +Content-Type: application/json +``` + +```json +{ + "status": "updated", + "milestone": { + "id": 1, + "action": "issue", + "numberRequired": 3, + "primaryBadge": { + "id": 4, + "slug": "milestone-badge-slug", + "name": "Milestone Badge Name", + "strapline": "Milestone strapline.", + "earnerDescription": "Milestone earner description.", + "consumerDescription": "Milesotne consumer description.", + "issuerUrl": "http://issuersite.com", + "rubricUrl": "http://issuersite.com/rubric", + "timeValue": 0, + "timeUnits": "minutes", + "limit": 0, + "unique": 0, + "created": "2014-05-29T21:24:32.000Z", + "type": "milestone badge type", + "archived": false, + "criteriaUrl": "http://issuersite.com/milestone-badge-slug/criteria", + "criteria": [ ], + "categories": [ ], + "tags": [ ], + "milestones": [ ] + }, + "supportBadges": [ + { + "id": 1, + "slug": "support-badge-slug", + ... + }, + ... + ] + } +} +``` + +#### Response structure + +* status +* milestone + * id + * action + * numberRequired + * primaryBadge ([badge](badges.md)) + * id + * slug + * ... + * supportBadges `[ ]` + * id + * slug + * ... + +#### Potential Errors + +* **Invalid data** + +``` + HTTP/1.1 400 Bad Request + Content-Type: application/json +``` + +```json + { + "code": "ValidationError", + "message": "Could not validate required fields", + "details": [ + { + "field": "id", + "value": "..." + }, + ... + ] + } +``` + +* **Milestone not found** + +``` +HTTP/1.1 404 Not Found +Content-Type: application/json +``` + +```json +{ + "code": "NotFoundError", + "message": "Could not find milestone with `id` " +} +``` + +## `DELETE /systems/:slug/milestones/:milestoneId` + +Delete an existing milestone. + +### Expected request + +``` +DELETE /systems//milestones/ +``` + +### Expected response + +```json +{ + "status": "deleted" +} +``` + +### Potential errors + +* **Milestone not found** + +``` +HTTP/1.1 404 Not Found +Content-Type: application/json +``` + +```json +{ + "code": "NotFoundError", + "message": "Could not find milestone with `id` " +} +``` diff --git a/docs/programs.md b/docs/programs.md index 7919e87..242fe29 100644 --- a/docs/programs.md +++ b/docs/programs.md @@ -1,13 +1,33 @@ # Programs -## Retrieve all programs, filtered by system and issuer. +Programs represent the lowest level of badging admin in BadgeKit. Each program belongs to one [issuer](issuers.md), optionally along with other programs. Badges can be associated with a program, which could be a program of events grouping activities on a theme or subject area. -Retrieves all available programs. +| NAME | VALUE | +|:---|:---| +| `id` | integer - _ID from database entry._ | +| `slug` | string - _Short, computer-friendly name for the program. Used to identify program in API endpoints_ | +| `url` | string - _URL for the program._ | +| `name` | string - _Name of the program._ | +| `description` | string - _A short, human-friendly description of the program._ | +| `email` | string - _Email address associated with the badge administrator of the program._ | +| `imageUrl` | string - _Image for the program._ | + +## Endpoints + +* [`GET /systems//issuers//programs`](#get-systemsslugissuersslugprograms) +* [`GET /systems//issuers//programs/`](#get-systemsslugissuersslugprogramsslug) +* [`POST /systems//issuers//programs`](#post-systemsslugissuersslugprograms) +* [`PUT /systems//issuers//programs/`](#put-systemsslugissuersslugprogramsslug) +* [`DELETE /systems//issuers//programs/`](#delete-systemsslugissuersslugprogramsslug) + +## `GET /systems//issuers//programs` + +Retrieves all available programs in the specified system and issuer. ### Expected request ``` -GET /systems/:systemSlug/issuers/:issuerSlug/programs HTTP/1.1 +GET /systems/:systemSlug/issuers/:issuerSlug/programs ``` ### Expected response @@ -15,31 +35,47 @@ GET /systems/:systemSlug/issuers/:issuerSlug/programs HTTP/1.1 ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "programs": [ { - "name": "Program Name", + "id": 1, "slug": "program-slug", - "description": "Program description" + "url": "http://programsite.com", + "name": "Program Name", + "description": "Program description.", + "email": "admin@programsite.com", + "imageUrl": "http://programsite.com/image.jpg" }, ... ] } ``` +#### Response structure + +* programs `[ ]` + * id + * slug + * url + * name + * description + * email + * imageUrl ### Potential errors *None* -## Retrieve a specific program +## `GET /systems//issuers//programs/` -Retrieves a specific program. +Retrieves a specific program using its slug. ### Expected request ``` -GET /systems/:systemSlug/issuers/:issuerSlug/programs/ HTTP/1.1 +GET /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug ``` ### Expected response @@ -47,45 +83,61 @@ GET /systems/:systemSlug/issuers/:issuerSlug/programs/ HTTP/1.1 ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "program": { - "name": "Program Name", + "id": 1, "slug": "program-slug", - "description": "Program Description" + "url": "http://programsite.com", + "name": "Program Name", + "description": "Program description.", + "email": "admin@programsite.com", + "imageUrl": "http://programsite.com/image.jpg" } } ``` +#### Response structure + +* program + * id + * slug + * url + * name + * description + * email + * imageUrl + ### Potential errors * **Program not found** - ``` +``` HTTP/1.1 404 Not Found Content-Type: application/json +``` +```json { "code": "ResourceNotFound", - "message": "Could not find program with slug ``" + "message": "Could not find program field: `slug`, value: " } - ``` +``` -## Create a new program +## `POST /systems//issuers//programs` Creates a new program. ### Expected request ``` -POST /systems/:systemSlug/issuers/:issuerSlug/programs HTTP/1.1 +POST /systems/:systemSlug/issuers/:issuerSlug/programs ``` Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. - - - | Parameters | Required | Description | |:-----------------------|-----------------|-------------------------| | **slug** | required | Short, computer-friendly name for the program. Good slugs are lowercase and use dashes instead of spaces, e.g. `cpl-rahms-readers`. Maximum of 50 characters and each program must have a unique slug. @@ -100,27 +152,45 @@ Requests can be sent as `application/json`, `application/x-www-form-urlencoded` ``` HTTP/1.1 201 Created Content-Type: application/json +``` +```json { "status": "created", "program": { + "id": 1, "slug": "program-slug", + "url": "http://programsite.com", "name": "Program Name", - "url": "https://example.org/program/", - "email": "program-badges@example.org", - "description": "Program Description" + "description": "Program description.", + "email": "admin@programsite.com", + "imageUrl": "http://programsite.com/image.jpg" } } ``` +#### Response structure + +* status +* program + * id + * slug + * url + * name + * description + * email + * imageUrl + ### Potential errors * **Invalid data** - ``` +``` HTTP/1.1 400 Bad Request Content-Type: application/json +``` +```json { "code": "ValidationError", "message": "Could not validate required fields", @@ -133,54 +203,70 @@ Content-Type: application/json ... ] } - ``` +``` * **Duplicate entry** - ``` +``` HTTP/1.1 409 Conflict Content-Type: application/json +``` +```json { "code": "ResourceConflict", - "error": program with that `slug` already exists", + "error": "program with that `slug` already exists", "details": { "slug": "program-slug", "name": "Program Name", - "url": "https://example.org/program/", - "email": "program-badges@example.org", - "description": "Program Description" + "url": "http://programsite.com", + "email": "admin@programsite.com", + "description": "Program description." } } - ``` +``` -## Update an existing program +## `PUT /systems//issuers//programs/` Updates an existing program. ### Expected request ``` -PUT /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug HTTP/1.1 +PUT /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug ``` + Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. -See above for parameters. You only have to pass in the fields you are updating. Any fields that are not represented will be left unchanged. +| Parameters | Description | +|:-----------------------|--------------------------| +| **slug** | Short, computer-friendly name for the program. Good slugs are lowercase and use dashes instead of spaces, e.g. `cpl-rahms-readers`. Maximum of 50 characters and each program must have a unique slug. +| **name** | Name of the program. Maximum of 255 characters. +| **url** | URL for the program. Must be fully qualified, e.g. https://www.example.org, **NOT** just www.example.org. +| **description** | A short, human-friendly description of the program. Maximum of 255 characters +| **email** | Email address associated with the badge administrator of the program. +| **image** | Image for the program. Should be either multipart data or a URL. + +You only have to pass in the fields you are updating. Any fields that are not represented will be left unchanged. ### Expected response ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "status": "updated", "program": { + "id": 1, "slug": "program-slug", - "name": Updated Program Name", - "url": "https://example.org/program/", - "email": "program-badges@example.org", - "description": "Updated Program Description" + "url": "http://programsite.com", + "name": "Updated Program Name", + "description": "Updated program description.", + "email": "admin@programsite.com", + "imageUrl": "http://programsite.com/image.jpg" } } ``` @@ -189,10 +275,12 @@ Content-Type: application/json * **Invalid data** - ``` +``` HTTP/1.1 400 Bad Request Content-Type: application/json +``` +```json { "code": "ValidationError", "message": "Could not validate required fields", @@ -205,35 +293,37 @@ Content-Type: application/json ... ] } - ``` +``` * **Duplicate entry** - ``` +``` HTTP/1.1 409 Conflict Content-Type: application/json +``` +```json { "code": "ResourceConflict", "error": "program with that `slug` already exists", "details": { "slug": "program-slug", "name": "Program Name", - "url": "https://example.org/program/", - "email": "program-badges@example.org", - "description": "Program Description" + "url": "http://programsite.com", + "email": "admin@programsite.com", + "description": "Program description." } } - ``` +``` -## Delete a program +## `DELETE /systems//issuers//programs/` Deletes an existing program. ### Expected request ``` -DELETE /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug HTTP/1.1 +DELETE /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug ``` ### Expected response @@ -241,15 +331,17 @@ DELETE /systems/:systemSlug/issuers/:issuerSlug/programs/:programSlug HTTP/1.1 ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "status": "deleted", "program": { "slug": "program-slug", "name": "Program Name", - "url": "https://example.org/program/", - "email": "program-badges@example.org", - "description": "Program Description" + "url": "http://programsite.com", + "email": "admin@programsite.com", + "description": "Program description." } } ``` @@ -258,12 +350,14 @@ Content-Type: application/json * **Program not found** - ``` +``` HTTP/1.1 404 Not Found Content-Type: application/json +``` +```json { "code": "ResourceNotFound", - "message": "Could not find program with slug ``" + "message": "Could not find program field: `slug`, value: " } - ``` +``` diff --git a/docs/systems.md b/docs/systems.md index b9a7309..8b2e890 100644 --- a/docs/systems.md +++ b/docs/systems.md @@ -1,13 +1,34 @@ # Systems +A system represents the top admin level within BadgeKit - an instance can contain one or more systems, a system can contain one or more issuers and an issuer can contain one or more programs. All BadgeKit API needs to function is one system, which badges will be automatically included in. Badges can optionally be associated with a system, issuer or program. + +| NAME | VALUE | +|:---|:---| +| `id` | integer - _ID from database entry._ | +| `slug` | string - _Short, computer-friendly name for the system. Used to identify system in API endpoints._ | +| `url` | string - _URL for the system._ | +| `name` | string - _Name of the system._ | +| `description` | string - _A short, human-friendly description of the system._ | +| `email` | string - _Email address associated with the badge administrator of the system._ | +| `imageUrl` | string - _Image for the system._ | +| `issuers` | array - _[Issuers](issuers.md) in the system (may contain [programs](programs.md))._ | + +## Endpoints + +* [`GET /systems`](#get-systems) +* [`GET /systems/`](#get-systemsslug) +* [`POST /systems`](#post-systems) +* [`PUT /systems/`](#put-systemsslug) +* [`DELETE /systems/`](#delete-systemsslug) + ## `GET /systems` -Retrieves all available systems. +Retrieves all available systems in the BadgeKit API instance. ### Expected request ``` -GET /systems HTTP/1.1 +GET /systems ``` ### Expected response @@ -15,31 +36,74 @@ GET /systems HTTP/1.1 ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "systems": [ - { - "name": "System Name", - "slug": "system-slug", - "description": "System description" - }, + { + "id": 1, + "slug": "system-slug", + "url": "http://systemsite.com", + "name": "System Name", + "description": "System description.", + "email": "admin@systemsite.com", + "imageUrl": "http://systemsite.com/image.jpg", + "issuers": [ + { + "id": 1, + "slug": "issuer-slug", + "url": "http://issuersite.com", + "name": "Issuer Name", + "description": "Issuer description.", + "email": "admin@issuersite.com", + "imageUrl": "http://issuersite.com/image.jpg", + "programs": [ + { + "id": 1, + "slug": "program-slug", + "url": "http://programsite.com", + "name": "Program Name", + "description": "Program description.", + "email": "admin@programsite.com", + "imageUrl": "http://programsite.com/image.jpg" + }, + ... + ] + }, + ... + ] + }, ... ] } ``` +#### Response structure + +* systems `[ ]` + * id + * slug + * url + * name + * description + * email + * imageUrl + * [issuers](issuers.md) `[ ]` + * [programs](programs.md) `[ ]` + ### Potential errors *None* ## `GET /systems/` -Retrieves a specific system. +Retrieves a specific system using its slug. ### Expected request ``` -GET /systems/ HTTP/1.1 +GET /systems/:systemSlug ``` ### Expected response @@ -47,29 +111,74 @@ GET /systems/ HTTP/1.1 ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "system": { - "name": "System Name", + "id": 1, "slug": "system-slug", - "description": "System Description" + "url": "http://systemsite.com", + "name": "System Name", + "description": "System description.", + "email": "admin@systemsite.com", + "imageUrl": "http://systemsite.com/image.jpg", + "issuers": [ + { + "id": 1, + "slug": "issuer-slug", + "url": "http://issuersite.com", + "name": "Issuer Name", + "description": "Issuer description.", + "email": "admin@issuersite.com", + "imageUrl": "http://issuersite.com/image.jpg", + "programs": [ + { + "id": 1, + "slug": "program-slug", + "url": "http://programsite.com", + "name": "Program Name", + "description": "Program description.", + "email": "admin@programsite.com", + "imageUrl": "http://programsite.com/image.jpg" + }, + ... + ] + }, + ... + ] } } ``` +#### Response structure + +* system + * id + * slug + * url + * name + * description + * email + * imageUrl + * [issuers](issuers.md) `[ ]` + * [programs](programs.md) `[ ]` + ### Potential errors * **System not found** - ``` - HTTP/1.1 404 Not Found - Content-Type: application/json +``` +HTTP/1.1 404 Not Found +Content-Type: application/json +``` - { - "code": "ResourceNotFound", - "message": "Could not find system with slug ``" - } - ``` +```json +{ + "code": "ResourceNotFound", + "message": "Could not find system field: `slug`, value: " +} +``` ## `POST /systems` @@ -77,10 +186,11 @@ Creates a new system. ### Expected request -Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. - +``` +POST /systems +``` - +Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. | Parameters | Required | Description | |:-----------------------|-----------------|-------------------------| @@ -96,27 +206,47 @@ Requests can be sent as `application/json`, `application/x-www-form-urlencoded` ``` HTTP/1.1 201 Created Content-Type: application/json +``` +```json { "status": "created", "system": { + "id": 1, "slug": "system-slug", "name": "System Name", - "url": "https://example.org/system/", - "email": "system-badges@example.org", - "description": "System Description" + "description": "System description.", + "url": "http://systemsite.com", + "email": "admin@systemsite.com", + "imageUrl": "http://systemsite.com/image.jpg", + "issuers": [ ] } } ``` +#### Response structure + +* status + * system + * id + * slug + * name + * description + * url + * email + * imageUrl + * [issuers](issuers.md) `[ ]` + ### Potential errors * **Invalid data** - ``` +``` HTTP/1.1 400 Bad Request Content-Type: application/json +``` +```json { "code": "ValidationError", "message": "Could not validate required fields", @@ -129,22 +259,25 @@ Content-Type: application/json ... ] } - ``` +``` * **Duplicate entry** - ``` +``` HTTP/1.1 409 Conflict Content-Type: application/json +``` +```json { "code": "ResourceConflict", - "error": system with that `slug` already exists", + "error": "system with that `slug` already exists", "details": { "slug": "system-slug", "name": "System Name", - "url": "https://example.org/system/", - "email": "system-badges@example.org", + "description": "System description.", + "url": "http://systemsite.com", + "email": "admin@systemsite.com", "description": "System Description" } } @@ -156,24 +289,42 @@ Updates an existing system. ### Expected request +``` +PUT /systems/:systemSlug +``` + Requests can be sent as `application/json`, `application/x-www-form-urlencoded` or `multipart/form-data`. -See above for parameters. You only have to pass in the fields you are updating. Any fields that are not represented will be left unchanged. +| Parameters | Description | +|:-----------------------|--------------------------| +| **slug** | Short, computer-friendly name for the system. Good slugs are lowercase and use dashes instead of spaces, e.g. `city-of-chicago`. Maximum of 50 characters and each system must have a unique slug. +| **name** | Name of the system. Maximum of 255 characters. +| **url** | URL for the system. Must be fully qualified, e.g. https://www.example.org, **NOT** just www.example.org. +| **description** | A short, human-friendly description of the system. Maximum of 255 characters +| **email** | Email address associated with the badge administrator of the system. +| **image** | Image for the system. Should be either multipart data or a URL. + +You only have to pass in the fields you are updating. Any fields that are not represented will be left unchanged. ### Expected response ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "status": "updated", "system": { + "id": 1, "slug": "system-slug", - "name": Updated System Name", - "url": "https://example.org/system/", - "email": "system-badges@example.org", - "description": "Updated System Description" + "name": "System Name", + "description": "System description.", + "url": "http://systemsite.com", + "email": "admin@systemsite.com", + "imageUrl": "http://systemsite.com/image.jpg", + "issuers": [ ] } } ``` @@ -182,10 +333,12 @@ Content-Type: application/json * **Invalid data** - ``` +``` HTTP/1.1 400 Bad Request Content-Type: application/json +``` +```json { "code": "ValidationError", "message": "Could not validate required fields", @@ -198,26 +351,28 @@ Content-Type: application/json ... ] } - ``` +``` * **Duplicate entry** - ``` +``` HTTP/1.1 409 Conflict Content-Type: application/json +``` +```json { "code": "ResourceConflict", "error": "system with that `slug` already exists", "details": { "slug": "system-slug", "name": "System Name", - "url": "https://example.org/system/", - "email": "system-badges@example.org", - "description": "System Description" + "url": "http://systemsite.com", + "email": "admin@systemsite.com", + "description": "System description." } } - ``` +``` ## `DELETE /systems/` @@ -226,7 +381,7 @@ Deletes an existing system. ### Expected request ``` -DELETE /systems/ HTTP/1.1 +DELETE /systems/:systemSlug ``` ### Expected response @@ -234,15 +389,20 @@ DELETE /systems/ HTTP/1.1 ``` HTTP/1.1 200 OK Content-Type: application/json +``` +```json { "status": "deleted", "system": { + "id": 1, "slug": "system-slug", "name": "System Name", - "url": "https://example.org/system/", - "email": "system-badges@example.org", - "description": "System Description" + "description": "System description.", + "url": "http://systemsite.com", + "email": "admin@systemsite.com", + "imageUrl": "http://systemsite.com/image.jpg", + "issuers": [ ] } } ``` @@ -251,12 +411,14 @@ Content-Type: application/json * **System not found** - ``` +``` HTTP/1.1 404 Not Found Content-Type: application/json +``` +```json { "code": "ResourceNotFound", - "message": "Could not find system with slug ``" + "message": "Could not find system with slug " } - ``` +``` diff --git a/docs/webhooks.md b/docs/webhooks.md index dc0397d..91b30c4 100644 --- a/docs/webhooks.md +++ b/docs/webhooks.md @@ -1,38 +1,116 @@ # System Callbacks (Webhooks) -## Overview +The system level object includes the configuration of a single webhook URL which will receive notifications when events occur in that system. To configure a webhook in your BadgeKit API database, add a record to the `webhooks` table, indicating the URL to send the webhook notifications to, plus a secret you can use to authenticate the data received. For a more detailed overview of configuring your webhook, see [Application Review Webhooks - Configuring a Webhook](https://github.com/mozilla/badgekit-api/wiki/Application-Review-Webhooks#configuring-a-webhook). -The system level object includes the configuration of a single webhook url which will receive notifications when events occur in that system. Webhooks are configured in the [consumer model](https://github.com/mozilla/badgekit-api/blob/master/app/models/consumer.js). If you're reading this documentation and need to configure a webhook on [BadgeKit.org](http://badgekit.org) please contact your badgekit.org support person. +If you're reading this documentation and need to configure a webhook on [BadgeKit.org](http://badgekit.org) please contact your BadgeKit support person. + +BadgeKit will send data to your webhook URL when these events occur: + +* [An application review is submitted](#new-application-review-submitted) +* [A badge is awarded/issued (a badge instance is created)](#new-badge-instance-created) ## Authentication -Each call to the webhook will be signed with the shared key assigned to a system. The signature works exactly like calls to the BadgeKit API. For more information, see [authorization](authorization.md). +Each call to the webhook will be signed with the key you set in your webhooks database table record. The signature works exactly like calls to the BadgeKit API. For more information, see [authorization](authorization.md). -## New Badge Instance Created +## New Application Review Submitted -Called when an instance of a badge is created (when a badge is rewarded, not when a badge class is created). +Called when a review is submitted for a badge application. The API sends the webhook the action (which will be `review`), the application data, the review data and approved status. Issuers can then process the application, responding by communicating with the earner and/or issuing the badge. -### Message -``` +Note that when a review is submitted that approves the application, the badge is NOT automatically awarded. The receiver of this webhook will have to call back to BadgeKit API to award the badge if that is the desired behavior - see [Issuing - Create a Badge Instance](issuing.md#create-a-badge-instance). _For example, you may wish to communicate with the earner for confirmation that they wish to be awarded the badge._ Similarly, the badge application will remain active and reviewable until a call is made to BadgeKit API that sets a `processed` timestamp on the application - see [Assessment - Update an Application](assessment.md#update-an-application). + +### Example Message + +```json { - action: 'award', - uid: {unique badge instance 'slug'}, - email: {email of the awardee}, - assertionUrl: {assertion url}, - issuedOn: {date / time of issuance}, + "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": "badge-slug", + ... + }, + + "processed": null, + "evidence": [ ] + }, + + "review": + { + "id": 456, + "slug": "1234567abcdefg", + "author": "reviewer@adomain.com", + "comment": "Comment from reviewer to earner.", + "reviewItems": + [ + { + "criterionId": 221, + "satisfied": 1, + "comment": "Exactly right." + }, + ... + ] + }, + + "approved": true } ``` -## New Application Review Submitted +#### Message structure -Called when a review is submitted for a badge application. Note that when a review is submitted that approves the application, the badge is NOT automatically awarded. The receiver of this webhook will have to immediately call back to BadgeKit API to award the badge if that is the desired behavior. Similarly, the badge application will remain active and reviewable until a call is made to BadgeKit API that sets a 'processed' timestamp on the application. +* action +* [application](assessment.md#applications) + * [badge](badges.md) +* [review](assessment.md#reviews) +* approved -### Message -``` +## New Badge Instance Created + +In BadgeKit, issuing a badge to an earner means creating a [badge instance](issuing.md#create-a-badge-instance). When a badge instance is created, the API sends a message to the webhook including the details of the badge instance and assertion. The message includes the action (which will be `award`), the UID, the badge awarded, the earner email, the assertion URL, the date issued and an optional comment. + +The `award` action webhook is sent whenever a badge is issued, whether this involves an application/ review process, a claim code or a badge being issued directly to an email address. This allows issuers to carry out follow-up tasks with the earner, such as offering to push a new badge to a backpack. + +### Example Message + +```json { - action: 'review', - application: {application object} - review: {review object} - approved: {true/false, indicating whether the application was approved} + "action": "award", + "uid": "abcdefghijkl1234567890", + "badge": + { + "id": 11, + "slug": "badge-slug", + ... + }, + + "email": "anearner@adomain.com", + "assertionUrl": "http://issuersite.com/public/assertions/abcdefghijkl1234567890", + "issuedOn": 1400094712, + "comment": null } ``` + +#### Message structure + +* action +* uid +* [badge](badges.md) +* email +* assertionUrl +* issuedOn +* comment + +## Guides + +For more on handling the webhook data, see the following pages in the BadgeKit API wiki: + +* [Application Review Webhooks](https://github.com/mozilla/badgekit-api/wiki/Application-Review-Webhooks) +* [Badge Issued Webhooks](https://github.com/mozilla/badgekit-api/wiki/Badge-Issued-Webhooks)