-
Notifications
You must be signed in to change notification settings - Fork 13
Draft API Spec
This document is copied from Apiary, and the canonical version lives there. This page is more easily edited and referenced; apiary's design is optimized for APIs, but comments are lost easily. If you wish to comment on part of the API, please do it on this document by editing the page and adding your comment, prefixed with your Github username, like this:
@singingwolfboy: here's an example of a comment.
@ormsbee: As a sort of meta-comment about the API as a whole -- I would really like to see use cases here for the various API calls. Not use cases as in "I would like to see a list of courses", but use cases as in "the LMS needs to render a list of active courses that can be filtered on the following things for display on XYZ pages". Maybe this is already internalized for you folks since you're coming at this from the studio side of things, but in many cases it's not clear to me. For instance, when I think of snapshots, getting them by ID is definitely good, but one of the first things that comes to mind is a researcher being able to say "Hey, that's a funny pattern in the event logs -- get me the snapshot of this course as it existed at this timestamp." or "Get me all the snapshots that were live between X and Y times, along with the times they became live."
The edX API is designed to allow clients to introspect and manipulate XBlocks and their related models.
Conceptually, an XBlock is any component that knows how to render itself, at least in a certain context. XBlocks can contain other XBlocks. XBlocks can define their own schema for storing data, which must consist only of primitive types (string, int, list, mapping)
The "title" and "description" strings are meant to be displayed in a UI, and as such
should be translated into the language specified by the Accept-Language
header
in the request. If the server is unable to translate the strings into the requested
language, they will be returned untranslated.
Note: This API is read-only. XBlock types cannot be created, modified, or deleted through this API; available XBlock types are solely controlled by the server administrator(s). Other XBlock types may exist on other servers, but these API responses will only contain information about the XBlock types that this server supports.
-
Response 200 (application/json)
[{ "id": "thumbs", "version": "1.0", "title": "Thumbs", "description": "a simple control to indicate thumbs-up or thumbs-down, and aggregate responses", "schema": {}, "defaults": {} }, { "id": "randomize", "version": "0.1a", "title": "Randomize", "description": "randomize children", "schema": {}, "defaults": {} }, { "id": "schema_ex", "version": "2.5.4", "title": "Schema Type Example", "description": "just demonstrates all the different schema types", "schema": { "name": "string", "age": "int", "my_dict": { "a": "string" }, "list_of_strings": ["string"], "list_of_ints": ["int"] }, "defaults": { "name": "Empty" } }]
@ormsbee: Why are we exposing the XBlock Type's schema?
@ormsbee: Will XBlock Types have versions? Authors?
@ormsbee: What is the use case for this? Studio querying to see what XBlock types are available for authoring? If so, does it need any additional information about how to categorize them?
- Parameters
- id (required, string,
schema_ex
) The ID of the XBlock type; usually a short lowercase string.
- id (required, string,
-
Response 200 (application/json)
{ "id": "schema_ex", "version": "2.5.4", "title": "Schema Type Example", "description": "just demonstrates all the different schema types", "schema": { "name": "string", "age": "int", "my_dict": { "a": "string" }, "list_of_strings": ["string"], "list_of_ints": ["int"] }, "defaults": { "name": "Empty" } }
@ormsbee: Since this is the exact same information as what's in the listing, why would someone want to use this? Wouldn't they typically come through the list view to get here?
@ormsbee: I'm not sure if this is within the scope of the API, but once I'm at a detail view for an XBlock type, what can I do with this information? Is there a resource that can be linked off of here that would bring me to something where I could create a new content piece based off of this XBlock type? See its editor? A sample problem?
Conceptually, an index is simply a mapping of branches (such as "live", "draft", "honors", etc) to the latest snapshot for that branch. Indexes also contain permission information for who can read the index, and who can modify its contents (or the contents of its child snapshots).
The "display" hash is meant to be displayed in a UI, and as such
should be translated into the language specified by the Accept-Language
header
in the request. If the server is unable to translate the strings into the requested
language, they will be returned untranslated.
A Course is the definitive example of an index, but other types of indexes could also exist.
@ormsbee: The term "index" is really generic and only makes sense to people if they know git (and even then, it's not really the same thing). If the reason we're not using the term "course" is because we want to allow for things that are smaller than courses, then let's talk to the folks in services and come up with an acceptable generic term that covers both courses and smaller units of instruction.
This API endpoint can take several query parameters to filter results, as follows:
-
root={partial_id}
- Filter based on organization, department, etc. Index IDs are
dotted paths like
mit.eecs.1234.Fall2013
; you can consider indexes to exist in a tree where each node of the tree is a dot-separated section of the dotpath. For example, if the client requests/v1/indexes?root=mit.eecs
, the response will contain all indexes that start withmit.eecs
and exist under that node of the tree. This means thatmit.eecs.7001X
andmit.eecs.8910X.Dec2014
would match, butmit.eecs7001X
would not match (becauseeecs
is not the same aseecs7001X
).harvard.mit.eecs
also would not match, because it doesn't start with the givenpartial_id
.
- Filter based on organization, department, etc. Index IDs are
dotted paths like
-
status={status}
- Filter by the given
status
. For example, if the client requests/v1/indexes?status=cancelled
, only indexes where the status attribute equals the stringcancelled
will be returned. This parameter is case-sensitive.
- Filter by the given
starts_before={datetime}
starts_after={datetime}
ends_before={datetime}
-
ends_after={datetime}
- Filter by course start date and/or end datetime. The provided
datetime
must be an ISO8601-formatted string, either representing a date (YYYY-MM-DD
) or representing a datetime (YYYY-MM-DDTHH:MM:SSZ
). If provided as a date, it is assumed that the time component of the datetime is 00:00:00. Alternatively, clients may pass the stringNOW
to indicate an arbitrary datetime within a 5 minute delta of when that the request is received, orTODAY
to indicate a datetime corresponding to 00:00:00 on the current date. Note that these special strings must be passed in all capital letters. (NOW
's 5-minute delta is so that the response can be effectively cached.)
- Filter by course start date and/or end datetime. The provided
-
Response 200 (application/json)
[{ "id": "myu.compsci.db.sql.t1_2014", "status": "active", "created_by": 84, "created_on": "2013-05-18T07:20:51Z", "starts_on": "2013-06-18T07:20:51Z", "ends_on": "2013-05-18T07:20:51Z", "enrollment_starts_on": "2013-05-18T07:20:51Z", "enrollment_ends_on": "2013-05-18T07:20:51Z", "permissions": { "read": { "user": [84], "group": [], "world": false }, "write": { "user": [84], "group": [], "world": false } }, "branches": { "live": "1c82df57-6b6d-47a7-9f31-24c19d6c6236", "draft": "19450641-4f77-4646-a78e-6ee07a784fb3" }, "display": { "name": "Intro to SQL", "organization": "My University", "number": "101X", "run": "Fall 2014", "image": "/v1/assets/845/raw", "summary": "A short description of the course", "description": "A longer description of the course" } }, { "id": "myu.compsci.db.sql.t1_2015", "status": "development", "created_by": 83, "created_on": "2013-05-16T17:10:31Z", "starts_on": "2013-06-18T07:20:51Z", "ends_on": "2013-05-18T07:20:51Z", "enrollment_starts_on": "2013-05-18T07:20:51Z", "enrollment_ends_on": "2013-05-18T07:20:51Z", "permissions": { "read": { "user": [83], "group": [], "world": false }, "write": { "user": [83], "group": [], "world": false } }, "branches": { "live": "6173b78b-cc3e-440a-b84f-aed720f320e6", "draft": "cca0d370-37be-4168-90c3-7fc4a818e8fc", "honors": "29210e12-66c0-4dba-ac61-a74c35cbdc4e" }, "display": { "name": "Intro to SQL", "organization": "My University", "number": "101X", "run": "Fall 2015", "image": "/v1/assets/847/raw", "summary": "A short description of the course", "description": "A longer description of the course" } }, { "id": "anotheru.english.shakespeare.Spring2012", "status": "finished", "created_by": 56, "created_on": "2013-11-10T09:20:20Z", "starts_on": "2013-06-18T07:20:51Z", "ends_on": "2013-05-18T07:20:51Z", "enrollment_starts_on": "2013-05-18T07:20:51Z", "enrollment_ends_on": "2013-05-18T07:20:51Z", "permissions": { "read": { "user": [56], "group": [], "world": false }, "write": { "user": [56], "group": [], "world": false } }, "branches": { "live": "da8e5827-0fd0-4fb3-8ce4-a163598130e0", "reading_group": "624133f6-3a7a-43e7-afc9-d30824f24b28" }, "display": { "name": "Intro to Shakespeare", "organization": "Another University", "number": "101X", "run": "Spring 2012", "image": "/v1/assets/102/raw", "summary": "A short description of the course", "description": "A longer description of the course" } }]
This API endpoint is a synonym for
/v1/indexes?status=active&starts_after=NOW&ends_before=NOW
.
It is included as a convienence for clients, since the
developers of this API anticipate that this will be a common need.
-
Response 200 (application/json)
[{ "id": "myu.compsci.db.sql.t1_2014", "status": "active", "created_by": 84, "created_on": "2013-05-18T07:20:51Z", "starts_on": "2013-06-18T07:20:51Z", "ends_on": "2013-05-18T07:20:51Z", "enrollment_starts_on": "2013-05-18T07:20:51Z", "enrollment_ends_on": "2013-05-18T07:20:51Z", "permissions": { "read": { "user": [], "group": [], "world": false }, "write": { "user": [], "group": [], "world": false } }, "branches": { "live": "1c82df57-6b6d-47a7-9f31-24c19d6c6236", "draft": "19450641-4f77-4646-a78e-6ee07a784fb3" }, "display": { "name": "Intro to SQL", "organization": "My University", "number": "101X", "run": "Fall 2014", "image": "/v1/assets/102/raw", "summary": "A short description of the course", "description": "A longer description of the course" } }]
- Parameters
- id (required, locator string,
arizona.latin.spring-2014
)
- id (required, locator string,
Create a new index with the information provided. An index can only
be created at the given ID if the user sending the API request has
permission to administer the fully-qualified identifier. For example,
a user could not create a course under the mit
namespace unless
the user has permission to modify that namespace. New namespaces
can be arbitrarily created by anyone.
The "id" parameter in the request is optional; if specified, it must match the id in the URL for this request. The "branches" and "permissions" maps are both optional. If the "branches" is not included in the request; it will be populated by the server with one branch named "draft". If the "permissions" map is not included in the request, the default permissions are read and write access for the user that created the index, and no other permissions for anyone.
-
Request (application/json)
{ "id": "arizona.latin.spring-2014", "status": "development" }
-
Response 201 (application/json)
{ "id": "arizona.latin.spring-2014", "status": "development", "created_by": 89, "created_on": "2013-11-20T14:15:16Z", "starts_on": null, "ends_on": null, "enrollment_starts_on": null, "enrollment_ends_on": null, "permissions": { "read": { "user": [89], "group": [], "world": false }, "write": { "user": [89], "group": [], "world": false } }, "branches": { "draft": "0d72a223-8b56-417d-9f5d-dfacc5955f2d" }, "display": {} }
-
Response 200 (application/json)
{ "id": "arizona.latin.spring-2014", "status": "development", "created_by": 89, "created_on": "2013-11-20T14:15:16Z", "starts_on": null, "ends_on": null, "enrollment_starts_on": null, "enrollment_ends_on": null, "permissions": { "read": { "user": [89], "group": [], "world": false }, "write": { "user": [89], "group": [], "world": false } }, "branches": { "live": "6173b78b-cc3e-440a-b84f-aed720f320e6", "draft": "cca0d370-37be-4168-90c3-7fc4a818e8fc", "honors": "29210e12-66c0-4dba-ac61-a74c35cbdc4e" } }
-
Request (application/json)
{ "status": "cancelled" }
-
Response 200 (application/json)
{ "id": "arizona.latin.spring-2014", "status": "cancelled", "created_by": 89, "created_on": "2013-11-20T14:15:16Z", "starts_on": null, "ends_on": null, "enrollment_starts_on": null, "enrollment_ends_on": null, "permissions": { "read": { "user": [89], "group": [], "world": false }, "write": { "user": [89], "group": [], "world": false } }, "branches": { "live": "6173b78b-cc3e-440a-b84f-aed720f320e6", "draft": "cca0d370-37be-4168-90c3-7fc4a818e8fc", "honors": "29210e12-66c0-4dba-ac61-a74c35cbdc4e" } }
Note that a DELETE request to an index will not change its status
attribute to "deleted" -- it will really, truly delete the index.
Subsequent GET requests to the index's former ID will return an
HTTP 404 status code. To update the status
attribute, use a PUT
request to partially update the index.
-
Response 200
{"message": "deleted"}
- Parameters
- id (required, string,
myu.compsci.db.sql.t1_2014
)
- id (required, string,
-
Response 200 (application/json)
{ "live": "6173b78b-cc3e-440a-b84f-aed720f320e6", "draft": "cca0d370-37be-4168-90c3-7fc4a818e8fc", "honors": "29210e12-66c0-4dba-ac61-a74c35cbdc4e" }
-
Request (application/json)
{ "live": "cca0d370-37be-4168-90c3-7fc4a818e8fc" }
-
Response 200 (application/json)
{ "live": "cca0d370-37be-4168-90c3-7fc4a818e8fc", "draft": "cca0d370-37be-4168-90c3-7fc4a818e8fc", "honors": "29210e12-66c0-4dba-ac61-a74c35cbdc4e" }
- Parameters
- id (required, string,
myu.compsci.db.sql.t1_2014
) - name (required, string,
live
)
- id (required, string,
This also supports a query parameter:
-
at={datetime}
- An ISO8601 formatted date or datetime, or the strings
NOW
orTODAY
, as described in the/v1/indexes
endpoint. This will return a redirect to the snapshot for the branch as it existed at the provided datetime. If noat
parameter is passed, it defaults toNOW
.
- An ISO8601 formatted date or datetime, or the strings
- Response 302
-
Headers
Location: /v1/snapshots/cca0d370-37be-4168-90c3-7fc4a818e8fc
-
Body
{"id": "cca0d370-37be-4168-90c3-7fc4a818e8fc"}
-
Creates an empty snapshot identified by the branch name. The created snapshot has no parent snapshot, and no content. If this API endpoint is called on a branch that already has an existing snapshot, a new snapshot is still created, and the course's branch pointer is updated to point to this new snapshot.
- Response 201 (application/json)
-
Headers
Location: /v1/snapshots/0298b9f7-b8ef-4bcb-ab33-8f712aa520bd
-
Body
{ "message": "created", "id": "0298b9f7-b8ef-4bcb-ab33-8f712aa520bd" }
-
Point the branch at a new snapshot identified by the given ID. If the given ID does not refer to an existing snapshot, this will fail with a 400 status code.
-
Request (text/plain)
29210e12-66c0-4dba-ac61-a74c35cbdc4e
-
Response 200 (application/json)
{"message": "updated"}
An index must have at least one branch in it. If an API request attempts to delete the last branch in an index, the request will fail with an HTTP 400 status code.
-
Response 200
{"message": "deleted"}
A snapshot provides a versioned dictionary of all blocks directly included in a courseware subtree with their settings, children references, and edit info. It also includes pointers to the parent snapshot, the ancestor snapshot, the index that owns it, the user who created this snapshot.
Snapshots are immutable -- any operation to "modify" an existing snapshot will create a new snapshot instead. When a snapshot is created, it copies the permissions mapping from the index that owns it at the moment of its creation. Indexes may have their permissions change, but the permissions on a particular snapshot are set at its creation and may never change.
- Parameters
- id (required, UUID,
5884ec28-f2a8-4735-a9f9-d54158405af5
)
- id (required, UUID,
-
Response 200 (application/json)
{ "id": "b20693ac-0dbd-4d4f-8740-e29bfb965409", "parent": "5d566816-e5db-4506-9ab3-5cba6ff972ef", "ancestor": "560dbc35-de74-4801-83bd-73b4bddc7594", "index": "myu.compsci.db.sql.t1_2014", "created_by": 89, "created_on": "2013-10-24T16:14:53Z", "permissions": { "read": { "user": [89], "group": [], "world": false }, "write": { "user": [89], "group": [], "world": false } }, "root_xblock": "head12345", "xblocks": { "head12345": { "name": "Root Node", "children": ["chapter1", "chapter2"] }, "chapter1": { "name": "Chapter One", "children": ["chapter3"] } "chapter2": { "name": "Chapter Two", "children": [] }, "chapter3": { "name": "Chapter Three", "children": [] } } }
-
Request (application/json)
{ "xblocks": { "chapter3: { "name": "the REAL chapter three" } } }
-
Response 201 (application/json)
-
Headers
Location: /v1/snapshot/91d7000c-2685-4e20-a2f0-ab9e5d1428ec
-
Body
{ "message": "created", "location": "/v1/snapshot/91d7000c-2685-4e20-a2f0-ab9e5d1428ec" }
-
A list of the assets that are referenced in this snapshot. If the user is authenticated and a part of the index, this list will contain locked (private) assets, as well.
-
Response 200 (application/json)
[{ "id": 1, "filename": "edx-logo.png", "type": "image/pdf" }, { "id": 18, "filename": "edx101-intro.mov", "type": "video/quicktime" }]
- Parameters
- id (required, UUID,
5884ec28-f2a8-4735-a9f9-d54158405af5
)
- id (required, UUID,
-
Response 200 (application/json)
{ "head12345": { "name": "Root Node", "children": ["chapter1", "chapter2"] }, "chapter1": { "name": "Chapter One", "children": ["chapter3"] } "chapter2": { "name": "Chapter Two", "children": [] }, "chapter3": { "name": "Chapter Three", "children": [] } }
- Parameters
-
id (required, UUID,
5884ec28-f2a8-4735-a9f9-d54158405af5
) -
type_id (optional, string,
video
)The type_id must match one of the XBlock Type IDs.
-
-
Response 200 (application/json)
{ "chapter1": { "name": "Chapter One", "children": ["chapter3"] } "chapter3": { "name": "Chapter Three", "children": [] } }
Like snapshots, XBlock instances are immutable. Any operation that would modify an existing XBlock instance instead creates a new instance, and returns a reference to that newly-created instance.
type
refers to the ID of an XBlock Type, andtype_version
is refers
to the version of the XBlock Type. If type_version
is null or is
unspecified, it is assumed that the instance is an instance of the latest
version of this XBlock Type that the server supports.
- Parameters
- id (required, UUID,
a7e3233a-c19d-40d4-b450-2046bd099501
) The ID of the snapshot that contains this xblock, usually a UUID. - name (required, string,
chapter7
) The name of the xblock instance contained in the snapshot
- id (required, UUID,
Note that the id
and parent
fields are IDs and not URLs, even though they
may resemble URLs. That's why they do not start with /v1
.
-
Response 200 (application/json)
{ "id": "/snapshots/a7e3233a-c19d-40d4-b450-2046bd099501/xblocks/chapter7", "type": "schema_ex", "type_version": "2.5.4", "parent": "/snapshots/e78ff15e-da0b-4d77-845e-30a30d265106/xblocks/chapter7", "children": [], "name": "my_name", "age": 30, "my_dict": { "a": "b" }, "list_of_strings": ["foo", "bar", "baz"], "list_of_ints": [1, 2, 3, 4, 5] }
This will create a new snapshot based on this snapshot, with an empty
XBlock instance with name provided in the URL. The request must
specify a type
in the request body, which is the ID of an XBlock
type that this server supports. The "parent" attribute on the
newly-created XBlock instance will be null
.
In addition to the type
attribute, this request may contain other
attributes to set for the newly-created XBlock instance, based on
the schema of the XBlock type. Those attributes will be set on the
newly-created XBlock instance. Unspecified attributes will be set
based on the defaults of the XBlock type.
If this API endpoint is called on an existing XBlock, the newly-created snapshot will contain an empty XBlock instance instead, effectively resetting the XBlock instance. This is also the way to change the type of an XBlock instance.
-
Request (application/json)
{"type": "randomize"}
-
Response 201 (application/json)
-
Headers
Location: /v1/snapshots/b6bbab4a-3a21-485e-ab41-094488ae0847/xblocks/chapter7
-
Body
{ "message": "created", "location": "/v1/snapshots/b6bbab4a-3a21-485e-ab41-094488ae0847/xblocks/chapter7" }
-
Note that this will create a new snapshot based on this snapshot, and a new XBlock instance based on this XBlock instance. The response is simply a pointer to the newly-created XBlock instance under the newly-created snapshot.
The type
of an XBlock instance cannot be updated using this API endpoint.
To change the type
of an XBlock instance, replace it using the POST verb.
-
Request (application/json)
{"name": "a_different_name"}
-
Response 201 (application/json)
-
Headers
Location: /v1/snapshots/b6bbab4a-3a21-485e-ab41-094488ae0847/xblocks/chapter7
-
Body
{ "message": "created", "location": "/v1/snapshots/b6bbab4a-3a21-485e-ab41-094488ae0847/xblocks/chapter7" }
-
An XBlock instance can contain a lot of information. To reduce the size of the response, a client can specify that it is only interested in a subset of the fields in the XBlock instance. The API response will then only return those fields.
- Parameters
- id (required, UUID,
a7e3233a-c19d-40d4-b450-2046bd099501
) The ID of the snapshot that contains this xblock, usually a UUID. - name (required, string,
chapter7
) The name of the xblock instance contained in the snapshot - field_list (optional, comma-separated list of strings,
name,age
) A comma-separated list of field names to include in the response to this request. If thefield_list
attribute is specified, any fields not included in thefield_list
will not be included in the response.
- id (required, UUID,
-
Response 200 (application/json)
{ "name": "my_name", "age": 30 }
Anyone who uses edX: a learner, course-creator, content author, etc.
Note that the GET verb is not currently supported for this API; due to privacy concerns, it is not possible to get a list of all users in the system.
-
Request (application/json)
{ "name": "Cookie Monster", "roles": ["learner"] }
-
Response 201 (application/json)
-
Headers
Location: /v1/users/35
-
Body
{ "message": "created", "location": "/v1/users/35" }
-
Comment
-
Response 200 (application/json)
[{ "id": 1, "name": "Anant Agarwal", "roles": ["admin"] }, { "id": 2, "name": "Joe Learner", "roles": ["learner"] }, { "id": 3, "name": "Sarah Staff", "roles": ["course_creator", "learner"], }]
-
-
- Parameters
- id (required, int,
1
)
- id (required, int,
-
Response 200 (application/json)
{ "id": 1, "name": "Anant Agarwal", "roles": ["admin"] }
-
Request (application/json)
{"name": "Bigshot"}
-
Response 200 (application/json)
{ "id": 1, "name": "Bigshot", "roles": ["admin"] }
-
Response 200 (application/json)
{"message": "deleted"}
Groups of users
List existing groups
-
Response 200 (application/json)
[{ "id": 1, "users": [1,2,3] }, { "id": 2, "users": [5, 10, 1] }]
Create a new group.
-
Request (application/json)
{"users": [100, 4, 59, 3]}
-
Response 201 (application/json)
-
Headers
Location: /v1/groups/7
-
Body
{ "message": "created", "location": "/v1/groups/7" }
-
- Parameters
- id (required, int,
7
)
- id (required, int,
-
Response 200 (application/json)
{ "id": 7, "users": [100, 4, 59, 3] }
-
Request (application/json)
{"users": [100, 4, 59]}
-
Response 200 (application/json)
{ "id": 7, "users": [100, 4, 59] }
-
Response 200 (application/json)
{"message": "deleted"}
Images, video, audio, etc
Note that there is no API to get a list of private assets
This creates a placeholder entry, with size 0.
-
Request (application/json)
{ "filename": "hypnotoad.gif", "type": "image/gif" }
-
Response 201 (application/json)
-
Headers
Location: /v1/assets/7463
-
Body
{ "message": "created", "location": "/v1/assets/7463" }
-
Size is in bytes.
-
Response 200 (application/json)
[{ "id": 1, "filename": "edx-logo.png", "type": "image/png", "size": 2045, "locked": false }, { "id": 2, "filename": "edx-docs.pdf", "type": "application/pdf" "size": 573892, "locked": false }, { "id": 7463m "filename": "hypnotoad.gif", "type": "image/gif", "size": 0, "locked": false }]
- Parameters
- id (required, int,
1
)
- id (required, int,
-
Response 200 (application/json)
{ "id": 1, "filename": "edx-logo.png", "type": "image/png", "size": 2045, "locked": false }
Note that you cannot update the "size" attribute using this API, only by uploading a new file content.
-
Request (application/json)
{"locked": "true"}
-
Response 200 (application/json)
{ "id": 1, "filename": "edx-logo.png", "type": "image/png", "size": 2045, "locked": true }
-
Response 200 (application/json)
{"message": "deleted"}
- Parameters
- id (required, int,
1
)
- id (required, int,
-
Response 200 (image/png)
PNG content goes here
-
Request (image/png)
PNG content goes here
-
Response 200 (application/json)
{"message": "uploaded"}
-
Response 200 (application/json)
{"message": "erased"}