diff --git a/Dockerfile b/Dockerfile
index 7dbb8473e..29584e611 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -31,7 +31,6 @@ ADD ./Gemfile /rails_app/
ADD ./Gemfile.lock /rails_app/
RUN bundle config --global jobs `cat /proc/cpuinfo | grep processor | wc -l | xargs -I % expr % - 1`
-RUN gem update --system
RUN bundle install --without development test
ADD ./ /rails_app
diff --git a/Dockerfile.dev b/Dockerfile.dev
index d32b626ac..fa67743c9 100644
--- a/Dockerfile.dev
+++ b/Dockerfile.dev
@@ -19,7 +19,6 @@ ADD ./Gemfile /rails_app/
ADD ./Gemfile.lock /rails_app/
RUN bundle config --global jobs `cat /proc/cpuinfo | grep processor | wc -l | xargs -I % expr % - 1`
-RUN gem update --system
RUN bundle install
ADD ./ /rails_app
diff --git a/docs/source/includes/_classifications.md b/docs/source/includes/_classifications.md
new file mode 100644
index 000000000..a230c93d3
--- /dev/null
+++ b/docs/source/includes/_classifications.md
@@ -0,0 +1,222 @@
+# Classifications
+
+```json
+{
+ "classifications": [{
+ "id": 1001,
+ "created_at": "2014-08-24T22:24:32Z",
+ "updated_at": "2014-08-24T22:24:32Z",
+ "completed": false,
+ "metadata": {
+ "started_at": "2014-08-24T22:20:21Z",
+ "finished_at": "2014-08-24T22:24:31Z",
+ "user_agent": "cURL",
+ "user_language": "es_MX",
+ "workflow_version": "11.12"
+ },
+ "annotations": [
+ {
+ "task": "task-1",
+ "value": [10.4, 12.4, 13.2]
+ }
+ ],
+ "links": {
+ "user": "1",
+ "subjects": ["10"],
+ "workflow": "81",
+ "project": "2"
+ }
+ }],
+ "links": {
+ "classifications.user": {
+ "href": "/users/{classifications.user}",
+ "type": "classifications"
+ },
+ "classifications.project": {
+ "href": "/projects/{classifications.project}",
+ "type": "projects"
+ },
+ "classifications.workflow": {
+ "href": "/workflows/{classification.workflow}",
+ "type": "workflows"
+ },
+ "classifications.subject": {
+ "href": "/subjects/{classifications.subjects}",
+ "type": "subjects"
+ }
+ },
+}
+
+```
+
+A single Classification resource object. This represents a _user's_
+responses to a _workflow's_ questions about a _subject_.
+
+A classification has the following attributes:
+
+Attribute | Type | Description
+--------- | ---- | -----------
+id | integer | read-only
+created_at | string | read-only
+updated_at | string | read-only
+completed | string | read-only
+metadata | hash |
+gold_standard | boolean |
+annotations | array(hash) |
+
+Annotations is an array of maps of the form `{ "task": "task_key",
+"value": "question answer" }`. Metadata contains additional information about a
+classification including:
+
+- started_at
+- finished_at
+- user_agent
+- workflow_version
+- user_language
+
+
+## List classifications
+
+```http
+GET /api/classifications HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
+Only lists classifications the active user has made or projects the user has edit permissions for.
+
+Classifications have special collection routes that indicate the scope you would like to retrieve.
+
+Possible options are:
+
++ `/api/classifications/` default, will fetch the current user's past complete classifications.
++ `/api/classifications/incomplete` will fetch the current user's past incomplete classifications.
++ `/api/classifications/project` will fetch classifications from projects a user has 'edit' permissions for
++ `/api/classifications/gold_standard` will fetch gold standard classifications for all marked workflows
+
+Any of the scopes may be further filtered using the *project_id*, *workflow_id*
+and *user_group_id* parameters.
+
+### Parameters
+
++ page (optional, integer) ... index of the collection page, 1 is default
++ page_size (optional, integer) ... number of items on a page. 20 is default
++ sort (optional, string) ... fields to sort collection by. updated_at is default
++ project_id (optional, integer) ... only retrieve classifications for a specific project
++ workflow_id (optional, integer) ... only retrieve classifications for a specific workflow
++ user_group_id (optional, integer) ... only retrieve classifications for a specific user group
++ include (optional, string) ... comma separated list of linked resources to return with the collection
++ last_id (optional, integer) ... only classifications with ids greater than `last_id` will be returned (`/project` only, requires project_id)
+
+
+
+
+## Retrieve a single Classification
+
+```http
+GET /api/classifications/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
+A User may retrieve any classification, irrespective of the complete status.
+
+### Parameters
+
++ `id` (required, integer) ... integer id of the resource to retrieve
+
+
+
+## Create a Classification [POST]
+
+```http
+POST /api/classifications HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "classifications": {
+ "completed": false,
+ "metadata": {
+ "started_at": "2014-08-24T22:20:21Z",
+ "finished_at": "2014-08-24T22:24:31Z",
+ "user_agent": "cURL",
+ "user_language": "es_MX",
+ "workflow_version": "11.12"
+ },
+ "annotations": [
+ {
+ "task": "task-name",
+ "value": "Any type: string, hash, array, etc"
+ }
+ ],
+ "links": {
+ "subjects": ["11"],
+ "workflow": "81",
+ "project": "2"
+ }
+ }
+}
+```
+
+Create a classification by providing a JSON-API formatted object, that
+must include _metadata_, _annotations_ and a _links_ hash. Optionally, it
+may include the _completed_ field, which if not included defaults to true.
+The completed field is used to store half-completed classifications, so the user
+can later continue from where they stopped.
+
+The _links_ hash must contain a _subjects_ hash, a _project_ and a _workflow_.
+The _metadata_ hash must contain all the keys specified in the example.
+Please note, the _workflow_version_ should be the value returned from the
+specific workflow representation. The annotations array must be in the
+format specified in the example, i.e. an array of objects, containing a _task_ and a _value_.
+The _task_ can be anything and must not necessarily align with the tasks of the workflow
+(even though that is generally not advised).
+
+
+
+
+## Edit a single Classification [PUT]
+
+```http
+PUT /api/classifications/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "classifications": {
+ "annotations": [
+ {
+ "task": "task-1",
+ "value": [10.4, 12.4, 13.2]
+ },
+ {
+ "task": "workflow-2",
+ "value": "fishy"
+ }
+ ],
+ "completed": true
+ }
+}
+```
+
+A User may modify an incomplete classification. It should be marked as
+completed when done.
+
+The *annotations* attributes must be returned as a full representation
+of the annotations array.
+
+
+## Destroy a single Classification [DELETE]
+
+```http
+DELETE /api/classifications/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
+A User may delete an incomplete classification.
diff --git a/docs/source/includes/_collection_preferences.md b/docs/source/includes/_collection_preferences.md
new file mode 100644
index 000000000..2f5e764d9
--- /dev/null
+++ b/docs/source/includes/_collection_preferences.md
@@ -0,0 +1,104 @@
+# Collection preferences
+
+```json
+{
+ "collection_preferences": [{
+ "id": "942",
+ "preferences": {
+ "display": "grid"
+ },
+ "created_at": "2014-03-20T06:23:12Z",
+ "updated_at": "2014-04-21T08:22:22Z",
+ "links": {
+ "user" : "30",
+ "collection": "11"
+ }
+ }],
+ "links": {
+ "collection_preferences.user": {
+ "href": "/user/{collection_preferences.user}",
+ "type": "users"
+ },
+ "collection_preferences.collection": {
+ "href": "/collections/{collection_preferences.collection}",
+ "type": "collections"
+ }
+ }
+}
+```
+
+A Collection Preference resource captures a user's settings for a
+particular collection.
+
+It has the following attributes:
+
+- id
+- created_at
+- updated_at
+- preferences
+
+*id*, *created_at*, and *updated_at* are set the by the API. Collection
+ preferences are only visible to user they belong to.
+
+## List collection preferences
+
+All collections add a meta attribute hash containing paging information.
+
+### Parameters
+
++ page (optional, integer) ... the index of the page to retrieve default is 1
++ page_size (optional, integer) ... number of items to include on a page default is 20
++ sort (optional, string) ... field to sort by
++ user_id (optional, integer) ... user_id to see preferences for
++ collection_id (optional, integer) ... collection_id to see preferences for
++ include (optional, string) ... comma separated list of linked resources to load
+
+
+## Retrieve a single CollectionPreference [GET]
+
+### Parameters
+
++ include (optional, string) ... comma separated list of linked resources to load
+
+
+## Edit a CollectionPreference [PUT]
+
+```http
+PUT /api/collection_preferences/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "collection_preferences": {
+ "preferences": {
+ "receive_updates": false,
+ }
+ }
+}
+```
+
+Only the owning user may edit a Collection Preference resource. The preferences field may be edited. Editing the preferences field requires a full representation of the preferences object to be sent.
+
+## Create a CollectionPreference [POST]
+
+```http
+POST /api/collection_preferences HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "collection_preferences": {
+ "preferences": {
+ "display": "grid"
+ },
+ "links": {
+ "collection": "1"
+ }
+ }
+}
+```
+
+Creating a Collection Preference requires only a link to a collection. Optionally an object of settings for preferences may be included.
+
+Since a user can only create, read, or modify their own preferences the currently logged in user is always set as the linked user on creation.
+
diff --git a/docs/source/includes/_collection_roles.md b/docs/source/includes/_collection_roles.md
new file mode 100644
index 000000000..4e4d6b5e7
--- /dev/null
+++ b/docs/source/includes/_collection_roles.md
@@ -0,0 +1,156 @@
+# CollectionRole
+```json
+{
+ "links": {
+ "collection_roles.owner": {
+ "href": "/{collection_roles.owner.href}",
+ "type": "owners"
+ },
+ "collection_roles.collection": {
+ "href": "/collections/{collection_roles.collection}",
+ "type": "collections"
+ }
+ },
+ "meta": {
+ "collection_roles": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/collection_roles?page_size=2",
+ "previous_href": "/collection_roles?page=14page_size=2",
+ "next_href": "/collection_roles?page=2&page_size=2",
+ "last_href": "/collection_roles?page=14&page_size=2"
+ }
+ },
+ "collection_roles": [{
+ "id": "942",
+ "roles": ["collaborator"],
+ "created_at": "2014-03-20T06:23:12Z",
+ "updated_at": "2014-04-21T08:22:22Z",
+ "links": {
+ "collection": "11",
+ "owner": {
+ "id": "4",
+ "display_name": "Owner 4",
+ "type": "user_groups",
+ "href": "user_groups/4"
+ }
+ }
+ },{
+ "id": "949",
+ "roles": ["viewer"],
+ "created_at": "2014-08-20T06:23:12Z",
+ "updated_at": "2014-09-21T08:22:22Z",
+ "links": {
+ "collection": "81",
+ "owner": {
+ "id": "1",
+ "display_name": "Owner 1",
+ "type": "users",
+ "href": "users/1"
+ }
+ }
+ }]
+}
+```
+
+Resources related to Roles for _Panoptes Collections_
+A Collection Role resources contains an array of roles assigned to a user
+for a particular collection.
+
+It has the following attributes:
+
+Attribute | Type | Description
+--------- | ---- | -----------
+id | integer | read-only
+created_at | string | read-only
+updated_at | string | read-only
+roles | array(string) |
+
+*id*, *created_at*, and *updated_at* are set the by the API. Collection
+roles are visible to the collection owner and the user given roles.
+
+### Collection Role Types
+- `collaborator` - full access to edit or delete a collection
+- `viewer` - may view a private collection
+
+## List All Collection Roles
+
+A Collection of CollectionRole resources.
+
+All collections add a meta attribute hash containing paging
+information.
+
+CollectionRoles are returned as an array under *collection_roles*.
+
+```http
+GET /api/collection_roles HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+ + sort (optional, string) ... field to sort by
+ + user_id (optional, integer) ... user_id to see roles for
+ + collection_id (optional, integer) ... collection_id to see roles for
+ + include (optional, string) ... comma separate list of linked resources to load
+
+
+
+## Retrieve a single CollectionRole
+
+```http
+GET /api/collection_roles/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + id (required, integer) ... integer identifier of the collection role resource
+ + include (optional, string) ... comma separate list of linked resources to load
+
+## Create a CollectionRole
+```http
+
+POST /api/collection_roles/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "collection_roles": {
+ "roles": ["collaborator"],
+ "links": {
+ "collection": "1",
+ "user": "842"
+ }
+ }
+}
+```
+
+Creating a Collection Role resource requires a link to a user and a
+collection. You may also include an array of roles.
+
+
+## Edit a CollectionRole
+```http
+PUT /api/collection_roles/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "collection_roles": {
+ "roles": ["viewer"]
+ }
+}
+```
+A user with permissions to edit a collection may modify roles for other
+users in the collection. A user without edit permissions may not edit
+their own roles.
+
+Editing requires sending a full representation of the roles array.
diff --git a/docs/source/includes/_collections.md b/docs/source/includes/_collections.md
new file mode 100644
index 000000000..36d08a02e
--- /dev/null
+++ b/docs/source/includes/_collections.md
@@ -0,0 +1,189 @@
+# Collections
+
+```json
+{
+ "links": {
+ "collections.subjects": {
+ "href": "/subjects{?collection_id=collections.id}",
+ "type": "subjects"
+ }
+ },
+ "collections": [{
+ "id": "101",
+ "created_at": "2014-04-20T06:23:12Z",
+ "updated_at": "2014-04-20T06:23:12Z",
+ "name" : "flowers",
+ "display_name": "Lots of Pretty flowers",
+ "default_subject_src": "panoptes-uploads.zooniverse.org/production/subject_location/hash.jpeg",
+ "links": {
+ "owner": {
+ "id": "10",
+ "display_name": "Owner 10",
+ "href": "/users/10",
+ "type": "users"
+ }
+ }
+ }]
+}
+```
+
+A collection is a user curated set of subjects for a particular
+project.
+
+It has the following attributes:
+
+Attribute | Type | Description
+--------- | ---- | -----------
+id | integer | read-only
+display_name | string |
+name | string |
+created_at | string | read-only
+updated_at | string | read-only
+default_subject | id |
+
+
+*id*, *created_at*, and *updated_at* are set by the API.
+
+## List all collections
+```http
+GET /api/collections HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+ + sort (optional, string) ... field to sort by
+ + owner (optional, string) ... string name of either a user or user group to filter by
+
+All collections add a meta attribute hash containing paging
+information.
+Collections are returned as an array under *collections*.
+
+## Retrieve a single collection
+```http
+GET /api/projects/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + id (required, integer) ... integer id of the resource to retrieve
+ + display_name (string) ... project name filter
+
+## Create a Collection
+A Collection only needs a *display name* to be created. By default
+name will be the underscored and downcased version of *display_name*,
+and the current user will be set as the owner.
+
+Optionally a create request may include name, a link to an
+owner, and links to subjects.
+
+```http
+POST /api/collections HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "collections": {
+ "display_name": "flowers",
+ "links": {
+ "owner": {
+ "id" : "10",
+ "display_name": "Owner 10",
+ "type": "user_groups",
+ "href": "/user_groups/10"
+ }
+ }
+ }
+}
+```
+
+
+## Edit a Collection
+
+```http
+PUT /api/collections/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "collections": {
+ "name": "flower_power",
+ }
+}
+```
+A user may edit a collection they are the owner of or have edit
+permissions for. A user may edit a collection's name, or display_name,
+and may also send a full representation of a collections subject links
+or a single subject id to set the default subject.
+
+Sending subject links through a put is not recommend, especially if a
+collection has many subjects.
+
+Removing subjects from a collection does not destroy the subject record.
+
+
+## Destroy a Collection
+```http
+DELETE /api/collections/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+A user who is the owner of a collection or who has destroy permissions
+for a collection, may delete it.
+
+
+## Add subject links
+Add subjects to a collection.
+
+```http
+POST /api/collections/123/links/subjects HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "subjects": ["1", "2"]
+}
+```
+
+A user is permitted to add subject if they are the collection owner or
+have edit permissions.
+
+
+## Remove subject links
+```http
+DELETE /api/collections/123/links/subjects/1,2 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
+Remove subjects from a collection.
+
+A user is permitted to remove subjects if they are the collection
+owner or have edit permissions.
+
++ Parameters
+ + id (required, integer) ... id of collection to edit
+ + link_ids (required, string) ... comma separated list of ids to remove
+
+
+## Add default subject link
+
+```http
+POST /api/collections/123/links/default_subject HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "default_subject": "1"
+}
+```
+Links a default subject to a collection. This subject's first media
+location URL will be included in the serialized collection and used
+as the thumbnail. Update this attribute with `null` to use the first
+subject in the linked list instead.
+
+A user is permitted to add a default subject if they are the collection
+owner or have edit permissions.
\ No newline at end of file
diff --git a/docs/source/includes/_deprecated_aggregations.md b/docs/source/includes/_deprecated_aggregations.md
new file mode 100644
index 000000000..0deae67cc
--- /dev/null
+++ b/docs/source/includes/_deprecated_aggregations.md
@@ -0,0 +1,215 @@
+# Group Aggregation
+Resources related to setting preferences for _Panoptes Collections_
+
+## Aggregation [/aggregation/{id}{?include}]
+An Aggregation resource captures the results of the aggregation engine for
+the set of classifications for a particular workflow and subject.
+
+It has the following attributes:
+
+- id
+- created_at
+- updated_at
+- aggregation
+
+*id*, *created_at*, and *updated_at* are set the by the API. Aggregations are
+only visible to users with rights on the workflow's associated project.
+
++ Parameters
+ + id (required, integer) ... integer identifier of resource
+
++ Model
+
+ + Body
+
+ {
+ "links": {
+ "aggregation.workflow": {
+ "href": "/workflows/{aggregation.workflow}",
+ "type": "workflows"
+ },
+ "aggregation.subject": {
+ "href": "/subjects/{aggregation.subject}",
+ "type": "subjects"
+ }
+ },
+ "aggregations": [{
+ "id": "5",
+ "created_at": "2014-03-20T06:23:12Z",
+ "updated_at": "2014-04-21T08:22:22Z",
+ "aggregation": {
+ "mean": 1,
+ "std": 1,
+ "count": [1, 1, 1],
+ "workflow_version": "1.1"
+ },
+ "links": {
+ "workflow" : "3",
+ "subject": "4"
+ }
+ }]
+ }
+
+### Retrieve a single Aggregation [GET]
++ Parameters
+ + include (optional, string) ... comma separated list of linked resources to load
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
++ Response 200
+
+ [Aggregation][]
+
+### Edit an Aggregation [PUT]
+Only a user with rights on the workflow's project may edit an Aggregation resource.
+The aggregation field may be edited.
+
+Editing the aggregation field requires a full representation of the
+aggregation object to be sent.
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
+ + Body
+
+ {
+ "aggregations": {
+ "aggregation": {
+ "mean": 1,
+ }
+ }
+ }
+
++ Response 200
+
+ [Aggregation][]
+
+## Aggregation Collection [/aggregations{?workflow_id,subject_id,page,page_size,sort,include}]
+A Collection of Aggregation resources.
+
+All collections add a meta attribute hash containing paging
+information.
+
+Aggregations are returned as an array under *aggregations*.
+
++ Model
+
+ {
+ "links": {
+ "aggregation.workflow": {
+ "href": "/workflows/{aggregation.workflow}",
+ "type": "workflows"
+ },
+ "aggregation.subject": {
+ "href": "/subjects/{aggregation.subject}",
+ "type": "subjects"
+ }
+ },
+ "meta": {
+ "collection_preferences": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/collection_preferences?page_size=2",
+ "previous_href": "/collection_preferences?page=14page_size=2",
+ "next_href": "/collection_preferences?page=2&page_size=2",
+ "last_href": "/collection_preferences?page=14&page_size=2"
+ }
+ },
+ "aggregations": [{
+ "id": "5",
+ "created_at": "2014-03-20T06:23:12Z",
+ "updated_at": "2014-04-21T08:22:22Z",
+ "aggregation": {
+ "mean": 1,
+ "std": 1,
+ "count": [1, 1, 1],
+ "workflow_version": "1.1"
+ },
+ "links": {
+ "workflow" : "3",
+ "subject": "4"
+ }
+ }, {
+ "id": "6",
+ "created_at": "2014-03-20T06:23:12Z",
+ "updated_at": "2014-04-21T08:23:22Z",
+ "aggregation": {
+ "mean": 2,
+ "std": 3,
+ "count": [2, 1, 1],
+ "workflow_version": "1.2"
+ },
+ "links": {
+ "workflow" : "4",
+ "subject": "3"
+ }
+ }]
+ }
+
+### List all Aggregations [GET]
++ Parameters
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+ + sort (optional, string) ... field to sort by
+ + workflow_id (optional, integer) ... workflow to see aggregations for
+ + subject_id (optional, integer) ... subject_id to see aggregations for
+ + include (optional, string) ... comma separated list of linked resources to load
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
++ Response 200
+
+ [Aggregation Collection][]
+
+### Create a Aggregation [POST]
+Creating an Aggregation requires an aggregation hash with the _workflow_version_
+set. Please note, the _workflow_version_ should be the value returned from the
+specific aggregation workflow's representation. The workflow and subject links
+must be provided as well.
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
+ + Body
+
+ {
+ "aggregations": {
+ "aggregation": {
+ "mean": 1,
+ "std": 1,
+ "count": [1, 1, 1],
+ "workflow_version": "1.1"
+ },
+ "links": {
+ "workflow" : "3",
+ "subject": "4"
+ }
+ }
+ }
+
++ Response 201
+
+ [Aggregation][]
diff --git a/docs/source/includes/_deprecated_project_contents.md b/docs/source/includes/_deprecated_project_contents.md
new file mode 100644
index 000000000..c8bf357fa
--- /dev/null
+++ b/docs/source/includes/_deprecated_project_contents.md
@@ -0,0 +1,390 @@
+# Group ProjectContents
+Resources related to the translatable strings for _Panotpes Projects_.
+
+## ProjectContent [/project_contents/{id}{?include}]
+A Project Content resources contains all strings for a project for a
+particular language. This resource will normally only be accessed by
+project translators. Users will receive translated versions of
+projects based on their *Accept-Language* header or preferences.
+
+It has the following attributes
+
+- id
+- language
+- title
+- description
+- created_at
+- updated_at
+- introduction
+- science_case
+- team_members
+- guide
+
+*id*, *created_at*, and *updated_at* are created by the api
+server.
+
+*language* is a two or five character identifier where the first two
+characters are the [ISO 639](http://en.wikipedia.org/wiki/ISO_639)
+language codes. In the five character version, the middle character
+may be a "-" or "_" and the final two characters the [ISO 3166-1 alpha-2](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
+country code. This allows multiple translations for each language, for
+instance Simplified Chinese (zh_CN) vs Traditional Chinese (zh_TW or
+zh_HK).
+
++ Parameters
+ + id (required, integer) ... integer id of the resource
+
++ Model
+
+ + Body
+
+ {
+ "links": {
+ "project_content.project": {
+ "href": "/project/{project_content.project}",
+ "type": "projects"
+ }
+ },
+ "project_contents": [{
+ "id": "42",
+ "language": "en_UK",
+ "title": "A Labourious and Colourful project",
+ "description": "Lots of colours and labour go into this",
+ "introduction": "Text..",
+ "science_case": "More text..",
+ "team_memebrs": [{
+ "name": "Rocky",
+ "bio": "a bio",
+ "institution": "whatsmattau",
+ "twitter": "@rocky",
+ }],
+ "guide": {
+ "image": "http://asdfasdf.jpg.gif",
+ "explanation": "It's a bear!"
+ },
+ "created_at": "2014-03-20T06:23:12Z",
+ "updated_at": "2014-04-21T08:22:22Z",
+ "links": {
+ "project": "11"
+ }
+ }]
+ }
+
+
+### Retrieve a single ProjectContent [GET]
++ Parameters
+ + include (optional, string) ... comma separated list of linked resources to load
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
++ Response 200
+
+ [ProjectContent][]
+
+### Update a ProjectContent [PUT]
+Only users with edit permissions for the parent project or users who
+have the "translator" roles may edit project contents".
+
+The *team_members* and *guide* fields must be updated as a full
+representation. The *language* field is not editable once created.
+
+Project Contents that have the same language as their parent project's
+primary_language field may not be edited.
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
+ + Body
+
+ {
+ "project_contents": {
+ "title": "A Less Labourious Title"
+ }
+ }
+
++ Response 200
+
+ [ProjectContent][]
+
+### Destroy a ProjectConent [DELETE]
+Only users who edit permissions for the parent project may remove
+project content models.
+
+Project Contents that have the same language as their parent project's
+primary_language field may not be destroyed.
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
++ Response 204
+
+## ProjectContent Version [/project_contents/{project_contents_id}/versions/{id}]
+A Project Content Version resource represents a set of changes made to
+a Project Content resource.
+
+It has the following attributes:
+
+- id
+- changset
+- whodunnit
+- created_at
+
+It is not editable.
+
++ Model
+
+ + Body
+
+ {
+ "versions": [{
+ "id": "42",
+ "changeset": {
+ "title": ["A Colourful Project", "A Colorful Project"]
+ },
+ "whodunnit": "stuartlynn",
+ "created_at": "2014-03-20T06:23:12Z",
+ "links": {
+ "item": {
+ id: "101",
+ "href": "/project_contents/101",
+ "type": "project_contents"
+ }
+ }
+ }]
+ }
+
+### Retrieve a Single Version [GET]
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
++ Response 200
+
+ [ProjectContent Version][]
+
+
+## ProjectContent Version Collection [/project_contents/{project_contents_id}/versions{?page_size,page}]
+A collection of Project Content Version resources.
+
+All collections add a meta attribute hash containing paging
+information.
+
+Project Content Versions are returned as an array under *versions*.
+
++ Model
+
+ + Body
+
+ {
+ "meta": {
+ "versions": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/project_contents/101/versions?page_size=2",
+ "previous_href": "/project_contents/101/versions?page=14page_size=2",
+ "next_href": "/project_contents/101/versions/?page=2&page_size=2",
+ "last_href": "/project_contents/101/versions?page=14&page_size=2"
+ }
+ },
+ "versions": [{
+ "id": "42",
+ "changeset": {
+ "title": ["A Colourful Project", "A Colorful Project"]
+ },
+ "whodunnit": "stuartlynn",
+ "created_at": "2014-03-20T06:23:12Z",
+ "links": {
+ "item": {
+ id: "101",
+ "href": "/project_contents/101",
+ "type": "project_contents"
+ }
+ }
+ },{
+ "id": "43",
+ "changeset": {
+ "description": ["No Words Here!", "Words"]
+ },
+ "whodunnit": "edwardothegreat",
+ "created_at": "2014-03-20T06:23:12Z",
+ "links": {
+ "item": {
+ id: "101",
+ "href": "/project_contents/101",
+ "type": "project_contents"
+ }
+ }
+ }]
+ }
+
+### List all Project Content Versions [GET]
++ Parameters
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
++ Response 200
+
+ [ProjectContent Version Collection][]
+
+## ProjectContent Collection [/project_contents{?project_id,language,page,page_size,include}]
+A collection of Project Content resources.
+
+All collections add a meta attribute hash containing paging
+information.
+
+Project Contents are returned as an array under *project_contents*.
+
++ Model
+
+ + Body
+
+ {
+ "links": {
+ "project_contents.project": {
+ "href": "/project/{project_content.project}",
+ "type": "projects"
+ }
+ },
+ "meta": {
+ "project_contents": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/project_contents?page_size=2",
+ "previous_href": "/project_contents?page=14page_size=2",
+ "next_href": "/project_contents?page=2&page_size=2",
+ "last_href": "/project_contents?page=14&page_size=2"
+ }
+ },
+ "project_contents": [{
+ "id": "42",
+ "language": "en_UK",
+ "title": "A Labourious and Colourful project",
+ "description": "Lots of colours and labour go into this",
+ "introduction": "Text..",
+ "science_case": "More text..",
+ "team_memebrs": [{
+ "name": "Rocky",
+ "bio": "a bio",
+ "institution": "whatsmattau",
+ "twitter": "@rocky",
+ }],
+ "guide": {
+ "image": "http://asdfasdf.jpg.gif",
+ "explanation": "It's a bear!"
+ },
+ "created_at": "2014-03-20T06:23:12Z",
+ "updated_at": "2014-04-21T08:22:22Z",
+ "links": {
+ "project": "11"
+ }
+ },
+ {
+ "id": "43",
+ "language": "en_CA",
+ "title": "A Labourious and Colourful project",
+ "description": "Lots of colours and labour go into this",
+ "introduction": "Text..",
+ "science_case": "More text..",
+ "team_memebrs": [{
+ "name": "Rocky",
+ "bio": "a bio",
+ "institution": "whatsmattau",
+ "twitter": "@rocky",
+ }],
+ "guide": {
+ "image": "http://asdfasdf.jpg.gif",
+ "explanation": "It's a bear!"
+ },
+ "created_at": "2014-03-20T06:23:12Z",
+ "updated_at": "2014-04-21T08:22:22Z",
+ "links": {
+ "project": "11"
+ }
+ }]
+ }
+
+### List all ProjectContents [GET]
++ Parameters
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+ + project_id (optional, integer) ... project_id to see contents for
+ + language (optional, string) ... language code to search for
+ + include (optional, string) ... comma separated list of linked resources to load
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
++ Response 200
+
+ [ProjectContent Collection][]
+
+### Create ProjectContent [POST]
+A ProjectContent resource can be created for a project by either a
+user with edit permissions for the project or a user with a
+"translator" role.
+
+The *language* field and a link to a project are the only required
+fields to created a ProjectContent resource.
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
+ + Body
+
+ {
+ "project_contents": {
+ "language": "es",
+ "links": {
+ "project": "11"
+ }
+ }
+ }
+
++ Response 201
+
+ [ProjectContent][]
+
+# Group WorkflowContents
+Resources related to the translatable strings for _Panotpes Workflows_.
diff --git a/docs/source/includes/_deprecated_workflow_contents.md b/docs/source/includes/_deprecated_workflow_contents.md
new file mode 100644
index 000000000..8164d2fae
--- /dev/null
+++ b/docs/source/includes/_deprecated_workflow_contents.md
@@ -0,0 +1,386 @@
+## WorkflowContent [/workflow_contents/{id}{?include}]
+A Workflow Content resource contains strings for a workflow in a
+particular language.
+
+This resource will normally only be accessed by
+project translators. Users will receive translated versions of
+workflows based on their *Accept-Language* header or preferences.
+
+It has the following attributes
+
+- id
+- language
+- created_at
+- updated_at
+- strings
+
+*id*, *created_at*, and *updated_at* are created by the api
+server.
+
+*language* is a two or five character identifier where the first two
+characters are the [ISO 639](http://en.wikipedia.org/wiki/ISO_639)
+language codes. In the five character version, the middle character
+may be a "-" or "_" and the final two characters the [ISO 3166-1 alpha-2](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
+country code. This allows multiple translations for each language, for
+instance Simplified Chinese (zh_CN) vs Traditional Chinese (zh_TW or
+zh_HK).
+
++ Parameters
+ + id (required, integer) ... integer id of the resource
+
++ Model
+
+ + Body
+
+ {
+ "links": {
+ "workflow_contents.workflow": {
+ "href": "/workflows/{workflow_content.workflow}",
+ "type": "workflows"
+ }
+ },
+ "workflow_contents": [{
+ "id": "43",
+ "strings": [
+ "a string",
+ "oh look",
+ "another one"
+ ],
+ "language": "en_US",
+ "created_at": "2014-03-20T06:23:12Z",
+ "updated_at": "2014-04-21T08:22:22Z",
+ "links": {
+ "workflow": "11"
+ }
+ }]
+ }
+
+
+### Retrieve a single WorkflowContent [GET]
++ Parameters
+ + include (optional, string) ... comma separated list of linked resources to load
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
++ Response 200
+
+ [WorkflowContent][]
+
+### Update a WorkflowContent [PUT]
+Only users with edit permissions for the parent project or users who
+have the "translator" roles may edit workflow contents.
+
+The *strings* field must be edited as a full representation. The
+*language* field may not be changed.
+
+Workflow Contents that have the same language as their parent workflow's
+primary_language field may not be edited.
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
+ + Body
+
+ {
+ "workflow_contents": {
+ "strings": [
+ "a replacement string"
+ ]
+ }
+ }
+
++ Response 200
+
+ [WorkflowContent][]
+
+### Destroy a WorkflowConent [DELETE]
+Only users who edit permissions for the parent project may remove
+workflow content models.
+
+Workflow Contents that have the same language as their parent workflow's
+primary_language field may not be destroyed.
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
++ Response 204
+
+## WorkflowContent Version [/workflow_contents/{workflow_contents_id}/versions/{id}]
+A Workflow Content Version resource represents a set of changes made to
+a Workflow Content resource.
+
+It has the following attributes:
+
+- id
+- changset
+- whodunnit
+- created_at
+
+It is not editable.
+
++ Parameters
+ + workflow_contents_id (required, integer) ... id of the workflow to retrieve versions for
+ + id (required, integer) ... integer id of the version to load
+
++ Model
+
+ + Body
+
+ {
+ "versions": [{
+ "id": "42",
+ "changeset": {
+ "strings": [[
+ "a string",
+ "another string",
+ "stringer bell"
+ ],[
+ "a string",
+ "another string",
+ "Stringer Bell"
+ ]]
+ },
+ "whodunnit": "stuartlynn",
+ "created_at": "2014-03-20T06:23:12Z",
+ "links": {
+ "item": {
+ "id": "101",
+ "href": "/workflow_contents/101",
+ "type": "workflow_contents"
+ }
+ }
+ }]
+ }
+
+### Retrieve a Single Version [GET]
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
++ Response 200
+
+ [WorkflowContent Version][]
+
+
+## WorkflowContent Version Collection [/workflow_contents/{workflow_contents_id}/versions{?page_size,page}]
+A collection of Workflow Content Version resources.
+
+All collections add a meta attribute hash containing paging
+information.
+
+Workflow Content Versions are returned as an array under *versions*.
+
++ Parameters
+ + workflow_contents_id (required, integer) ... id of the workflow to retrieve versions for
+
++ Model
+
+ + Body
+
+ {
+ "meta": {
+ "versions": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/workflow_contents/101/versions?page_size=2",
+ "previous_href": "/workflow_contents/101/versions?page=14page_size=2",
+ "next_href": "/workflow_contents/101/versions/?page=2&page_size=2",
+ "last_href": "/workflow_contents/101/versions?page=14&page_size=2"
+ }
+ },
+ "versions": [{
+ "id": "42",
+ "changeset": {
+ "strings": [[
+ "a string",
+ "another string",
+ "stringer bell"
+ ],[
+ "a string",
+ "another string",
+ "Stringer Bell"
+ ]]
+ },
+ "whodunnit": "stuartlynn",
+ "created_at": "2014-03-20T06:23:12Z",
+ "links": {
+ "item": {
+ "id": "101",
+ "href": "/workflow_contents/101",
+ "type": "workflow_contents"
+ }
+ }
+ },{
+ "id": "43",
+ "changeset": {
+ "strings": [[
+ "a string",
+ "another string",
+ "Stringer Bell"
+ ],[
+ "a string",
+ "a brother string",
+ "Stringer Bell"
+ ]]
+ },
+ "whodunnit": "stuartlynn",
+ "created_at": "2014-03-20T06:23:12Z",
+ "links": {
+ "item": {
+ "id": "101",
+ "href": "/workflow_contents/101",
+ "type": "workflow_contents"
+ }
+ }
+ }]
+ }
+
+### List all Workflow Content Versions [GET]
++ Parameters
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
++ Response 200
+
+ [WorkflowContent Version Collection][]
+
+## WorkflowContent Collection [/workflow_contents{?workflow_id,language,page,page_size,include}]
+A collection of Workflow Content resources.
+
+All collections add a meta attribute hash containing paging
+information.
+
+Workflow Contents are returned as an array under *workflow_contents*.
+
++ Model
+
+ + Body
+
+ {
+ "links": {
+ "workflow_contents.workflow": {
+ "href": "/project/{workflow_content.workflow}",
+ "type": "workflows"
+ }
+ },
+ "meta": {
+ "workflow_contents": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/workflow_contents?page_size=2",
+ "previous_href": "/workflow_contents?page=14page_size=2",
+ "next_href": "/workflow_contents?page=2&page_size=2",
+ "last_href": "/workflow_contents?page=14&page_size=2"
+ }
+ },
+ "workflow_contents": [{
+ "id": "43",
+ "strings": [
+ "a string",
+ "oh look",
+ "another one"
+ ],
+ "language": "en_US",
+ "created_at": "2014-03-20T06:23:12Z",
+ "updated_at": "2014-04-21T08:22:22Z",
+ "links": {
+ "workflow": "11"
+ }
+ },{
+ "id": "44",
+ "strings": [
+ "a string",
+ "oh look",
+ "another one"
+ ],
+ "language": "en_US",
+ "created_at": "2014-03-20T06:23:12Z",
+ "updated_at": "2014-04-21T08:22:22Z",
+ "links": {
+ "workflow": "12"
+ }
+ }]
+ }
+
+### List all WorkflowContents [GET]
++ Parameters
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+ + workflow_id (optional, integer) ... id of workflow to see contents for
+ + language (optional, string) ... language code to search for
+ + include (optional, string) ... comma separated list of linked resources to load
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
++ Response 200
+
+ [WorkflowContent Collection][]
+
+### Create WorkflowContent [POST]
+A WorkflowContent resource can be created for a workflow by either a
+user with edit permissions for the parent project or a user with a
+"translator" role.
+
+The *language* field and a link to a workflow are the only required
+fields to create a WorkflowContent resource.
+
++ Request
+
+ + Headers
+
+ Accept: application/vnd.api+json; version=1
+ Content-Type: application/json
+
+ + Body
+
+ {
+ "workflow_contents": {
+ "language": "es",
+ "links": {
+ "workflow": "11"
+ }
+ }
+ }
+
++ Response 201
+
+ [WorkflowContent][]
diff --git a/docs/source/includes/_memberships.md b/docs/source/includes/_memberships.md
new file mode 100644
index 000000000..7df785de3
--- /dev/null
+++ b/docs/source/includes/_memberships.md
@@ -0,0 +1,155 @@
+# Memberships
+```json
+{
+ "links": {
+ "memberships.user_group": {
+ "href": "/user_groups/{memberships.user_group}",
+ "type": "user_groups"
+ },
+ "memberships.user": {
+ "href": "/users/{memberships.user}",
+ "type": "users"
+ }
+ },
+ "meta": {
+ "memberships": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/memberships?page_size=2",
+ "previous_href": "/memberships?page=14page_size=2",
+ "next_href": "/memberships?page=2&page_size=2",
+ "last_href": "/memberships?page=14&page_size=2"
+ }
+ },
+ "memberships": [{
+ "id": "101",
+ "created_at": "2014-04-20T06:23:12Z",
+ "updated_at": "2014-04-20T06:23:12Z",
+ "state": "active",
+ "roles": ["group_admin"],
+ "links": {
+ "user": "12",
+ "user_groups": "31"
+ }
+ },{
+ "id": "111",
+ "created_at": "2014-04-20T06:23:12Z",
+ "updated_at": "2014-04-20T06:23:12Z",
+ "state": "inactive",
+ "roles": [],
+ "links": {
+ "user": "12",
+ "user_groups": "20"
+ }
+ }]
+}
+```
+
+A membership represents and user's status in a group and their role
+within the group.
+
+It has the following attributes:
+
+Attribute | Type | Description
+--------- | ---- | -----------
+id | integer | read-only
+created_at | string | read-only
+updated_at | string | read-only
+state | string |
+roles | array(string) |
+
+
+*id*, *created_at*, and *update_at* are assigned by the API.
+
++ Membership *state* can be:
+ + "invited"
+ + "active"
+ + "inactive"
+
+When a user is added to a group, their state is set to "invited". After they take
+action to join the group their state becomes "active". A User who leaves
+a group has their state set to "inactive".
+
+## List All Memberships
+```http
+GET /api/memberships HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + page (optional, integer) ... index of the page to retrieve 1 by default
+ + page_size (optional, integer) ... number of items per page 20 by default
+ + sort (optional, string) ... field to sort by, id by default
+ + user_id (optional, integer) ... filter list to memberships for a user
+ + user_group_id (optional, integer) ... filter list to memberships for a user group
+
+All memberships add a meta attribute hash containing paging
+information.
+
+Memberships are returned as an array under *memberships*.
+
+
+## Retreive a Membership
+```http
+GET /api/memberships/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + id (required, integer) ... integer id of the resource to retrieve
+
+## Create a Membership
+```http
+POST /api/memberships HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "memberships": {
+ "join_token": "decafbad",
+ "links": {
+ "user": "10",
+ "user_group": "11
+ }
+ }
+}
+```
+A membership creation request must include a link to a user and to a
+user_group, although currently the linked user must always be the current user.
+The request must also include the secret join_token of the user_group as an attribute
+of the membership (although this property is not persisted).
+
+
+## Edit a Membership
+```http
+PUT /api/memberships/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "memberships": {
+ "state": "inactive"
+ }
+}
+```
+A user can ordinary only change their membership state. A user with
+user group edit permissions can change the membership's roles.
+
+
+## Destroy a Membership
+```http
+DELETE /api/memberships/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+Destroying a membership only sets the state to inactive. A user may
+destroy their own memberships and a user with edit permission in a
+user group may destroy membership for that group.
+
diff --git a/docs/source/includes/_organizations.md b/docs/source/includes/_organizations.md
new file mode 100644
index 000000000..5c1661d98
--- /dev/null
+++ b/docs/source/includes/_organizations.md
@@ -0,0 +1,261 @@
+# Organizations
+
+```json
+{
+ "organizations": [
+ {
+ "id": "5",
+ "display_name": "United Federation of Projects",
+ "description": "Every project in the galaxy",
+ "introduction": "Hello and welcome to the UFP",
+ "title": "United Federation of Projects",
+ "href": "/organizations/5",
+ "primary_language": "en",
+ "listed_at": null,
+ "listed": true,
+ "slug": "user/slug",
+ "urls": [
+ {
+ "url": "https://twitter.com/UFP",
+ "path": "United Federation of Twitter",
+ "site": "twitter.com/",
+ "label": ""
+ }
+ ],
+ "categories": [],
+ "announcement": "Oh Gosh!",
+ "links": {
+ "organization_contents": [
+ "5"
+ ],
+ "organization_roles": [
+ "9999"
+ ],
+ "projects": [
+ "1",
+ "2"
+ ],
+ "owner": {
+ "id": "811067",
+ "display_name": "meredithspalmer",
+ "type": "users",
+ "href": "/users/811067"
+ },
+ "pages": [
+ "5"
+ ],
+ "avatar": {
+ "href": "/organizations/5/avatar",
+ "type": "avatars",
+ "id": "27687087"
+ },
+ "background": {
+ "href": "/organizations/5/background",
+ "type": "backgrounds",
+ "id": "30335947"
+ },
+ "attached_images": {
+ "href": "/organizations/5/attached_images",
+ "type": "attached_images"
+ }
+ }
+ }
+ ],
+ "links": {
+ "organizations.attached_images": {
+ "href": "/organizations/{organizations.id}/attached_images",
+ "type": "media"
+ },
+ "organizations.organization_contents": {
+ "href": "/organization_contents?organization_id={organizations.id}",
+ "type": "organization_contents"
+ },
+ "organizations.organization_roles": {
+ "href": "/organization_roles?organization_id={organizations.id}",
+ "type": "organization_roles"
+ },
+ "organizations.projects": {
+ "href": "/projects?organization_id={organizations.id}",
+ "type": "projects"
+ },
+ "organizations.pages": {
+ "href": "/organizations/{organizations.id}/pages",
+ "type": "organization_pages"
+ },
+ "organizations.owner": {
+ "href": "/{organizations.owner.href}",
+ "type": "owners"
+ },
+ "organizations.avatar": {
+ "href": "/organizations/{organizations.id}/avatar",
+ "type": "media"
+ },
+ "organizations.background": {
+ "href": "/organizations/{organizations.id}/background",
+ "type": "media"
+ }
+ },
+ "meta": {
+ "organizations": {
+ "page": 1,
+ "page_size": 20,
+ "count": 1,
+ "include": [],
+ "page_count": 1,
+ "previous_page": null,
+ "next_page": null,
+ "first_href": "/organizations",
+ "previous_href": null,
+ "next_href": null,
+ "last_href": "/organizations"
+ }
+ }
+}
+```
+
+An Organization is a collection of projects that are related by dicipline, research group
+
+It has the following attributes:
+
+Attribute | Type | Description
+--------- | ---- | -----------
+id | integer | read-only
+display_name | string |
+title | string |
+description | string |
+introduction | string |
+slug | string |
+primary_language | string |
+listed_at | datetime |
+activated_state | integer |
+created_at | datetime | read-only
+updated_at | datetime | read-only
+urls | jsonb |
+listed | boolean |
+categories | string |
+available_languages | array(string) |
+background | string |
+avatar | string |
+
+## List All Organizations
+```http
+GET /api/organizations HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
++ Parameters
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+ + sort (optional, string) ... field to sort by
+ + owner (optional, string) ... string owner name of either a user or a user group to filter by.
+ + include (optional, string) ... comma separated list of linked resources to include in the response
+
+Response a *meta* attribute hash containing
+paging information.
+
+Organizations are returned as an array under the _organizations_ key.
+
+
+## Retrieve a single Organization
+```http
+GET /api/organizations/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
++ Parameters
+ + id (required, integer) ... integer id of the resource to retrieve
+ + include (optional, string) ... comma separated list of linked resources to include in the response
+ + display_name (optional, string)...name filter
+ + listed (boolean) ... publicly visible
+
+
+## Create a Organization
+```http
+POST /api/organizations HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "Organizations": {
+ "display_name": "United Federation of Projects",
+ "description": "Lots o' Projects",
+ "primary_language": "en-us",
+ "links": {
+ "projects": ["1", "2"]
+ }
+ }
+}
+```
+
+Requires at least a *display_name*, *description* and primary_language*.
+
+
+## Edit a single Organization
+```http
+PUT /api/organizations/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "organizations": {
+ "display_name": "Klingon Empire",
+ "links": {
+ "workflows": ["1"],
+ "subject_sets": ["10"]
+ }
+ }
+}
+```
+
+A User must be the owner of a Organization or have update
+permissions to edit the resource.
+
+Setting has may links through a PUT, while supported, is not
+recommended. Instead, use the link endpoints explained below.
+
+
+## Destroy a single Organization
+```http
+DELETE /api/organizations/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+A user may destroy a Organization they own or have destroy permissions for.
+
+## Add Organization Links
+```http
+POST /api/organizations/123/links/projects HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "projects": ["1", "2"]
+}
+```
+The body key must match the link_type parameter.
+
++ Parameters
+ + id (required, integer) - the id of the project to add
+ + link_type (required, string)
+ the name of the link to edit
+ + Members
+ + `projects`
+
+
+## Destroy a Link
+```http
+DELETE /api/organizations/123/links/projects/1,2 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+The recommended way to destroy Organization links.
+Will destroy the comma separated list of link ids for the given link
+type.
+
++ Parameters
+ + id (required, integer) ... the id of the project to modify
+ + link_type (required, string)
+ the name of the link to edit
+ + Members
+ + `projects`
+ + link_ids (required, string) ... comma separated list of ids to destroy
\ No newline at end of file
diff --git a/docs/source/includes/_project_preferences.md b/docs/source/includes/_project_preferences.md
new file mode 100644
index 000000000..4947dd8db
--- /dev/null
+++ b/docs/source/includes/_project_preferences.md
@@ -0,0 +1,171 @@
+# Project Preferences
+```json
+ {
+ "links": {
+ "project_preferences.user": {
+ "href": "/user/{project_preferences.user}",
+ "type": "users"
+ },
+ "project_preferences.project": {
+ "href": "/projects/{project_preferences.project}",
+ "type": "projects"
+ }
+ },
+ "meta": {
+ "project_preferences": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/project_preferences?page_size=2",
+ "previous_href": "/project_preferences?page=14page_size=2",
+ "next_href": "/project_preferences?page=2&page_size=2",
+ "last_href": "/project_preferences?page=14&page_size=2"
+ }
+ },
+ "project_preferences": [{
+ "id": "942",
+ "email_communication": true,
+ "preferences": {
+ "tutorial": true
+ },
+ "created_at": "2014-03-20T06:23:12Z",
+ "updated_at": "2014-04-21T08:22:22Z",
+ "links": {
+ "user" : "30",
+ "project": "11"
+ }
+ },{
+ "id": "949",
+ "email_communication": true,
+ "preferences": {
+ "tutorial": true
+ },
+ "created_at": "2014-08-20T06:23:12Z",
+ "updated_at": "2014-09-21T08:22:22Z",
+ "links": {
+ "user" : "33",
+ "project": "81"
+ }
+ }]
+}
+```
+
+A Project Preference resource captures a user's settings for a
+particular project.
+
+It has the following attributes:
+
+Attribute | Type | Description
+--------- | ---- | -----------
+id | integer | read-only
+created_at | datetime | read-only
+updated_at | datetime | read-only
+preferences | jsonb |
+email_communication | boolean |
+
+*id*, *created_at*, and *updated_at* are set the by the API. Project
+ preferences are only visible to user they belong to.
+
+
+## List all ProjectPreferences
+```http
+GET /project_preferences HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
++ Parameters
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+ + sort (optional, string) ... field to sort by
+ + user_id (optional, integer) ... user_id to see preferences for
+ + project_id (optional, integer) ... project_id to see preferences for
+ + include (optional, string) ... comma separated list of linked resources to load
+
+Responses will have meta attribute hash containing paging information.
+
+ProjectPreferences are returned as an array under *project_preferences*.
+
+
+## Retrieve a single ProjectPreference
+```http
+GET /api/project_preferences/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
++ Parameters
+ + id (required, integer) ... integer identifier of resource
+ + include (optional, string) ... comma separated list of linked resources to load
+
+## Create a ProjectPreference
+```http
+POST /api/project_preferences HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "project_preferences": {
+ "email_communication": true,
+ "preferences": {
+ "tutorial": true
+ },
+ "links": {
+ "project": "1"
+ }
+ }
+}
+```
+Creating a Project Preference requires only a link to a
+project. Optionally a boolean flag for email_communication or a hash
+of settings for preferences may be included.
+
+Since a user can only create, read, or modify their own preferences
+the currently logged in user is always set as the linked user on
+creation.
+
+## Edit a ProjectPreference
+```http
+PUT /api/project_preferences/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "project_preferences": {
+ "preferences": {
+ "mini_course": false,
+ }
+ }
+}
+```
+Only the owning user may edit a Project Preference resource. The
+email_communication field and the preferences field may be edited.
+
+Editing the preferences field requires a full representation of the
+preferences hash to be sent.
+
+
+## Edit ProjectPreferences Settings
+```http
+POST /api/project_preferencess?user_id=1&project_id=1 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "project_preferences": {
+ "settings": {
+ "workflow_id": 1234,
+ }
+ }
+}
+```
+Project owners may edit the settings attribute of any user's Project Preferences associated
+with that project (and only that project). You need to provide the "user_id" and the "project_id"
+to specify the resource to apply the settings update to. Note: in the settings payload the
+"workflow_id" is the only accepted parameter.
+
++ Parameters
+ + user_id (string) ... The id of the user whose preference needs updating
+ + project_id (string) ... The id of the project the preference setting should be scoped to
diff --git a/docs/source/includes/_project_roles.md b/docs/source/includes/_project_roles.md
new file mode 100644
index 000000000..9438f3374
--- /dev/null
+++ b/docs/source/includes/_project_roles.md
@@ -0,0 +1,148 @@
+# Project Roles
+```json
+{
+ "links": {
+ "project_roles.owner": {
+ "href": "/{project_roles.owner.href}",
+ "type": "owners"
+ },
+ "project_roles.project": {
+ "href": "/projects/{project_roles.project}",
+ "type": "projects"
+ }
+ },
+ "meta": {
+ "project_roles": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/project_roles?page_size=2",
+ "previous_href": "/project_roles?page=14page_size=2",
+ "next_href": "/project_roles?page=2&page_size=2",
+ "last_href": "/project_roles?page=14&page_size=2"
+ }
+ },
+ "project_roles": [{
+ "id": "942",
+ "roles": ["collaborator"],
+ "created_at": "2014-03-20T06:23:12Z",
+ "updated_at": "2014-04-21T08:22:22Z",
+ "links": {
+ "project": "11",
+ "owner": {
+ "id": "3",
+ "display_name": "Owner 3",
+ "type": "users",
+ "href": "users/3"
+ }
+ }
+ },{
+ "id": "949",
+ "roles": ["tester", "translator"],
+ "created_at": "2014-08-20T06:23:12Z",
+ "updated_at": "2014-09-21T08:22:22Z",
+ "links": {
+ "project": "81",
+ "owner": {
+ "id": "33",
+ "display_name": "Owner 33",
+ "type": "users",
+ "href": "users/33"
+ }
+ }
+ }]
+}
+```
+A Project Role resources contains an array of roles assigned to a user
+for a particular project
+
+It has the following attributes:
+
+Attribute | Type | Description
+--------- | ---- | -----------
+id | integer | read-only
+roles | array(string) |
+created_at | datetime | read-only
+updated_at | datetime | read-only
+
+
+*id*, *created_at*, and *updated_at* are set the by the API. Project
+roles are visible to the project owner and the user given roles.
+
++ **Roles for a project may be:**
+ + collaborator - full access to edit or delete a project
+ + tester - Able to see a private project
+ + scientist - Able to moderate project Talk and see a private project
+ + moderator - Able to moderate project Talk and see a private project
+ + translator - Able to create new and edit project and workflow translations
+
+## List all ProjectRoles
+```http
+GET /api/project_roles HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
++ Parameters
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+ + sort (optional, string) ... field to sort by
+ + user_id (optional, integer) ... user_id to see roles for
+ + project_id (optional, integer) ... project_id to see roles for
+ + include (optional, string) ... comma separate list of linked resources to load
+
+Response will have a meta attribute hash containing paging
+information.
+
+ProjectRoles are returned as an array under *project_roles*.
+
+## Retrieve a single ProjectRole
+```http
+GET /api/project_roles/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
++ Parameters
+ + id (required, integer) ... integer identifier of the project role resource
+ + include (optional, string) ... comma separate list of linked resources to load
+
+## Create a ProjectRole
+```http
+POST /api/project_roles HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "project_roles": {
+ "roles": ["collaborator"],
+ "links": {
+ "project": "11",
+ "user": "30"
+ }
+ }
+}
+```
+Creating a Project Role resource requires a link to a user and a
+project. You may also include an array of roles.
+
+
+## Edit a ProjectRole
+```http
+PUT /api/project_roles/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "project_roles": {
+ "roles": ["tester", "translator"]
+ }
+}
+```
+A user with permissions to edit a project may modify roles for other
+users in the project. A user without edit permissions may not edit
+their own roles.
+
+Editing requires sending a full representation of the roles array.
diff --git a/docs/source/includes/_set_member_subjects.md b/docs/source/includes/_set_member_subjects.md
new file mode 100644
index 000000000..97037614f
--- /dev/null
+++ b/docs/source/includes/_set_member_subjects.md
@@ -0,0 +1,151 @@
+# SetMemberSubjects
+```json
+{
+ "links": {
+ "set_member_subjects.subject": {
+ "href": "/subjects/{set_member_subjects.subject}",
+ "type": "subjects"
+ },
+ "set_member_subjects.subject_set": {
+ "href": "/subject_sets/{set_member_subjects.subject_set}",
+ "type": "subject_sets"
+ }
+ },
+ "meta": {
+ "set_member_subjects": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/set_member_subjects?page_size=2",
+ "previous_href": "/set_member_subjects?page=14page_size=2",
+ "next_href": "/set_member_subjects?page=2&page_size=2",
+ "last_href": "/set_member_subjects?page=14&page_size=2"
+ }
+ },
+ "set_member_subjects": [{
+ "id": "1023",
+ "created_at": "2014-03-20T00:15:47Z",
+ "updated_at": "2013-09-30T10:20:32Z",
+ "state": "active",
+ "priority": 101231.1231,
+ "links": {
+ "subject": "1231",
+ "subject_set": "101"
+ }
+ },{
+ "id": "1024",
+ "created_at": "2014-03-20T00:15:47Z",
+ "updated_at": "2013-09-30T10:20:32Z",
+ "state": "retired",
+ "priority": 1231.1231,
+ "links": {
+ "subject": "1232",
+ "subject_set": "101"
+ }
+ }]
+}
+```
+
+A Set Member Subject resource contains the state of a subject that is
+included in a resource.
+
+It has the following attributes:
+
+Attribute | Type | Description
+--------- | ---- | -----------
+id | integer | read-only
+state | string |
+priority | float |
+created_at | datetime | read-only
+updated_at | datetime | read-only
+
+*id*, *created_at*, and *updated_at* are set by the API server.
+
+
+## List all SetMemberSubjects
+```http
+GET /api/set_member_subjects HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+ + subject_id (optional, integer) ... id of subject to see set_member_subjects for
+ + subject_set_id (optional, integer) ... id of subject_set to see set_member_subjects for
+ + include (optional, string) ... comma separated list of linked resources to load
+
+Response will contain a meta attribute hash containing paging information.
+
+SetMemberSubjects are returned as an array under *set_member_subjects*.
+
+
+## Retrieve a Single SetMemberSubject
+```http
+GET /api/set_member_subjects/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
++ Parameters
+ + id (required, integer) ... integer identifier for the resource
+ + include (optional, string) ... comma separated list of linked resources to load
+
+## Create a SetMemberSubject
+```http
+POST /api/set_member_subjects HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "set_member_subjects: {
+ "links": {
+ "subject": "12031",
+ "subject_set": "10"
+ }
+ }
+}
+```
+
+A SetMemberSubject may be created by a user that can see the Subject
+they wish to link to and can edit the project the SubjectSet belongs
+to.
+
+A SetMemberSubject requires links be provided to a Subject and a
+SubjectSet. Optionally, the create request may include a state and a
+priority. The state will be 'active' by default and the priority will
+be null by default.
+
+
+## Edit a SetMemberSubject
+```http
+PUT /api/set_member_subjects/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "set_member_subjects: {
+ "state": "inactive"
+ }
+}
+```
+
+A user with edit permissions for the project a SetMemberSubject's
+SubjectSet belongs to may edit the *state* and *priority* attributes.
+
+
+## Destroy a SetMemberSubject
+```http
+DELETE /api/set_member_subjects/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+A user with edit permissions for the project a SetMemberSubject's
+Subject belongs to may destroy a SetMemberSubject resource. This
+removes the linked Subject form the linked SubjectSet
+
+
diff --git a/docs/source/includes/_subject_sets.md b/docs/source/includes/_subject_sets.md
new file mode 100644
index 000000000..f9c7617bb
--- /dev/null
+++ b/docs/source/includes/_subject_sets.md
@@ -0,0 +1,218 @@
+# SubjectSets
+```json
+{
+ "links": {
+ "subject_sets.workflows": {
+ "href": "/workflows?subject_set_id={subject_sets.id}",
+ "type": "workflows"
+ },
+ "subject_sets.subjects": {
+ "href": "/subjects{?subject_set_id=subject_sets.id}",
+ "type": "subjects"
+ },
+ "subject_sets.set_member_subjects": {
+ "href": "/set_member_subjects{?subject_set_id=subject_sets.id}",
+ "type": "set_member_subjects"
+ },
+ "subject_sets.project": {
+ "href": "/project/{subject_sets.project}",
+ "type": "projects"
+ }
+ },
+ "meta": {
+ "subject_sets": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/subject_sets?page_size=2",
+ "previous_href": "/subject_sets?page=14page_size=2",
+ "next_href": "/subject_sets?page=2&page_size=2",
+ "last_href": "/subject_sets?page=14&page_size=2"
+ }
+ },
+ "subject_sets": [{
+ "id": "20",
+ "display_name": "Weird Looking Galaxies",
+ "metadata": {
+ "category": "things"
+ },
+ "created_at": "2014-02-13T10:11:34Z",
+ "updated_at": "2014-02-13T10:11:34Z",
+ "set_member_subject_count": 100,
+ "links": {
+ "project": "1",
+ "workflow": "10"
+ }
+ },{
+ "id": "20",
+ "display_name": "Boring Looking Galaxies",
+ "metadata": {
+ "category": "things"
+ },
+ "created_at": "2014-02-13T10:11:34Z",
+ "updated_at": "2014-02-13T10:11:34Z",
+ "set_member_subject_count": 100,
+ "links": {
+ "project": "1",
+ "workflow": "11"
+ }
+ }]
+}
+```
+
+Subject Sets represent collections of Subjects that are paired with a
+workflow of questions to be answered. A SubjectSet belongs to one
+Workflow, while a single Workflow may have many SubjectSets.
+
+A SubjectSet has the following attributes:
+
+Attribute | Type | Description
+--------- | ---- | -----------
+id | integer | read-only
+display_name | string |
+metadata | jsonb |
+set_member_subjects_count | integer |
+created_at | datetime | read-only
+updated_at | datetime | read-only
+
+All attributes except display_name are set by the API
+
+## List all Subject Sets
+```http
+GET /api/subject_sets HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + page (optional, integer) ... index of the page to retrieve 1 by default
+ + page_size (optional, integer) ... number of items per page 20 by default
+ + sort (optional, string) ... field to sort by, id by default
+ + project_id (optional, integer) ... filter by linked project
+ + workflow_id (optional, integer) ... filter by linked workflow
+ + include (optional, string) ... comma separated list of linked resources to include in the response
+
+Response contains a meta attribute hash containing paging
+information.
+
+Subject Sets are returned as an array under *subject_sets*.
+
+
+## Retrieve a single Subject Set
+```http
+GET /api/subject_sets/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + id (required, integer) ... integer id of the subject_set to retrieve
+ + include (optional, string) ... comma separated list of linked resources to include in the response
+
+## Create a Subject Set
+```http
+POST /api/subject_sets HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "subject_sets": {
+ "display_name": "A Group of Interesting Subjects",
+ "metadata": {
+ "category": "things"
+ },
+ "links": {
+ "project": "43",
+ "workflows": ["47"],
+ "subjects": ["234", "1243", "8023"]
+ }
+ }
+}
+```
+A subject set must supply a display_name and a link to a project. Optionally,
+it may include links to subjects and a single workflow.
+
+Instead of a list of subjects a SubjectSet may include a link to a
+Collection which will import the collection's subjects into a new
+SubjectSet.
+
+
+## Edit a single Subject Set
+```http
+PUT /api/subject_sets/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "subject_sets": {
+ "display_name": "Normal Galaxies",
+ "links": {
+ "subjects": ["1", "2", "4", "5", "10"]
+ }
+ }
+}
+```
+
+A user may only edit a subject if they edit permissions for the parent
+project. The display_name attributes and links to workflows and subjects are
+editable. Editing links requires a full representation of the new set
+of links, but does not destroy unlinked resources.
+
+This is NOT the recommended way to manage linked subjects.
+
+
+## Destroy a Subject Set
+```http
+DELETE /api/subject_sets/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
+A user may only destroy a subject set if they have destroy permissions
+for the subject set's project.
+
+## Subject Set Links
+Allows the addition of links to subjects to a subject
+set object without needing to send a full representation of the linked
+relationship.
+
+**This is the recommended way to managed linked subjects.**
+
+## Add Subject Set Link
+```http
+POST /api/subject_sets/123/links/subjects HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "subjects": ["1", "5", "9", "11"]
+}
+```
+
++ Parameters
+ + id (required, integer) ... the id of the Subject Set to modify
+ + link_type (required, string) ... the relationship to modify must be the same as the supplied body key
+Only Subjects links may be edited.
+
+
+## Destroy Subject Set Links
+```http
+DELETE /api/subject_sets/123/links/subjects/1,2,3 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
+Allows links to be removed without sending a full representation of the
+linked relationship.
+
++ Parameters
+ + id (required, integer) ... the id of the Subject Set to modify
+ + link_type (required, string) ... the relationship to modify
+ + link_ids (required, integer) ... comma separated list of ids to remove
+
+Will only remove the link. This operation does not destroy the linked object.
+
diff --git a/docs/source/includes/_subject_workflow_statuses.md b/docs/source/includes/_subject_workflow_statuses.md
new file mode 100644
index 000000000..96433d18e
--- /dev/null
+++ b/docs/source/includes/_subject_workflow_statuses.md
@@ -0,0 +1,102 @@
+# SubjectWorkflowStatuses
+```json
+{
+ "links": {
+ "SubjectWorkflowStatuses.workflow": {
+ "href": "/workflows/{subject_workflow_statuses.workflow}",
+ "type": "workflows"
+ },
+ "SubjectWorkflowStatus.subject": {
+ "href": "/subjects/{subject_workflow_statuses.subject}",
+ "type": "subjects"
+ }
+ },
+ "meta": {
+ "collection_preferences": {
+ "page": 1,
+ "page_size": 2,
+ "count": 2,
+ "include": [],
+ "page_count": 1,
+ "previous_page": 0,
+ "next_page": 0,
+ "first_href": "/subject_workflow_statuses?page_size=2",
+ "previous_href": "",
+ "next_href": "",
+ "last_href": "/subject_workflow_statuses?page=2&page_size=2"
+ }
+ },
+ "subject_workflow_statuses": [{
+ "id": "1",
+ "created_at": "2014-03-20T06:23:12Z",
+ "updated_at": "2014-04-21T08:22:22Z",
+ "classifications_count": 10,
+ "retired_at": "2014-04-21T08:22:22Z",
+ "retirement_reason": "consensus",
+ "links": {
+ "workflow" : "3",
+ "subject": "4"
+ }
+ },{
+ "id": "2",
+ "created_at": "2014-03-21T06:23:12Z",
+ "updated_at": "2014-04-22T08:22:22Z",
+ "classifications_count": 2,
+ "retired_at": "2014-04-22T08:22:22Z",
+ "retirement_reason": "blank",
+ "links": {
+ "workflow" : "3",
+ "subject": "5"
+ }
+ }]
+}
+```
+
+A SubjectWorkflowStatus resource collates the status of a subject in a workflow.
+This status includes the classification count and the retirement state and reason.
+
+It has the following attributes:
+
+Attribute | Type | Description
+--------- | ---- | -----------
+id | integer | read-only
+classifications_count | integer |
+retired_at | datetime |
+retirement_reason | string |
+created_at | datetime | read-only
+updated_at | datetime | read-only
+
+*id*, *created_at*, and *updated_at* are set the by the API.
+
+SubjectWorkflowStatuses are
+only visible to users with rights on the workflow's associated project.
+
+## List all SubjectWorkflowStatuses
+```http
+GET /api/subject_workflow_statuses HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+ + workflow_id (optional, integer) ... workflow to see SubjectWorkflowStatuses for
+ + subject_id (optional, integer) ... subject_id to see SubjectWorkflowStatuses for
+ + include (optional, string) ... comma separated list of linked resources to load
+
+Response will have a meta attribute hash containing paging
+information.
+
+SubjectWorkflowStatuses are returned as an array under *subject_workflow_statuses*.
+
+
+## Retrieve a single SubjectWorkflowStatus
+```http
+GET /api/subject_workflow_statuses/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
++ Parameters
+ + id (required, integer) ... integer identifier of resource
+ + include (optional, string) ... comma separated list of linked resources to load
diff --git a/docs/source/includes/_subjects.md b/docs/source/includes/_subjects.md
new file mode 100644
index 000000000..f64758fc8
--- /dev/null
+++ b/docs/source/includes/_subjects.md
@@ -0,0 +1,310 @@
+# Subjects
+```json
+ {
+ "links": {
+ "subjects.project": {
+ "href": "/projects/subjects.project",
+ "type": "projects"
+ },
+ "subjects.subject_sets": {
+ "href": "/subject_sets/subjects.subject_sets",
+ "type": "subject_sets"
+ }
+ },
+ "meta": {
+ "subjects": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/subjects?page_size=2",
+ "previous_href": "/subjects?page=14page_size=2",
+ "next_href": "/subjects?page=2&page_size=2",
+ "last_href": "/subjects?page=14&page_size=2"
+ }
+ },
+ "subjects": [{
+ "id": "1",
+ "zooniverse_id": "AGFS0001231",
+ "created_at": "2014-03-24T10:42:21Z",
+ "updated_at": "2014-03-24T10:42:21Z",
+ "locations": [
+ {"image/jpeg": "http://s3.amazonaws.com/subjects/1.png"}
+ ],
+ "metadata": {
+ "lens_type": "50mm"
+ },
+ "links": {
+ "project": "1"
+ "subject_sets": ["1"]
+ }
+ },{
+ "id": "2",
+ "zooniverse_id": "AGFS0001232",
+ "created_at": "2014-03-24T10:44:21Z",
+ "updated_at": "2014-03-24T10:44:21Z",
+ "locations": [
+ {"image/jpeg": "http://s3.amazonaws.com/subjects/2.png"}
+ ],
+ "metadata": {
+ "lens_type": "50mm"
+ },
+ "links": {
+ "project": "1"
+ "subject_sets": ["1"]
+ }
+ }]
+ }
+```
+
+A single Subject object. A Subject is a resource that describe a
+piece of media to be classified including metadata about the object.
+
+Attribute | Type | Description
+--------- | ---- | -----------
+id | integer | read-only
+zooniverse_id | integer | read-only
+locations | array(hash) |
+metadata | hash |
+created_at | datetime | read-only
+updated_at | datetime | read-only
+
+*id*, *zooniverse_id*, *created_at*, and *updated_at* are assigned by
+the API.
+
+## List Subjects
+```http
+GET /api/subjects HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+ + sort (optional, string) ... field to sort by
+ + workflow_id (optional, integer) ... filter to subjects belonging to a specific workflow
+ + subject_set_id (optional, integer) ... return subjects belonging to the identified subject_set
+
+Response will have a *meta* attribute hash containing paging information.
+
+Subjects are returned as an array under the _subjects_ key.
+
+## Retrieve a single Subject
+```http
+GET /api/subjects/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + id (required, integer) ... integer id of the subject resource
+
+## Create a Subject
+```http
+POST /api/subjects HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "subjects": {
+ "locations": [
+ "image/png",
+ [
+ "video/webm",
+ "video/mp4"
+ ]
+ ],
+ "metadata": {
+ "lens_type": "50mm"
+ },
+ "links": {
+ "project": "1"
+ }
+ }
+}
+```
+
+A *locations* attribute and a project link are required.
+
+To have the Zooniverse host your media resources the *locations* array
+should have the mime-types of the subject's associated media,
+e.g `"locations":["image/png", "image/jpeg", "image/png"]`,
+note the locations mime types are stored in order.
+
+The create response will contain signed s3 urls the client may make a PUT
+request containing the media to. The signed urls will be valid for 20 minutes.
+Please take the order of the returned s3 urls into account when PUT'ing
+local media resources to the remote location.
+
+To use your own hosted media resources the *locations* array
+should be comprised of objects that represent the mime-type and the hosted URL
+of the subject's associated media,
+e.g. `"locations":[
+{"image/png": "https://your.s3_account.com/subjects/1.png"},
+{"image/jpeg": "https://your.s3_account.com/subjects/1.jpg"}
+]`.
+
+The *metadata* attribute is optional.
+
+## Edit a single Subject
+```http
+PUT /api/subjects/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "subjects": {
+ "locations": [
+ "image/png"
+ ]
+ }
+}
+```
+
+Users are permitted to edit subjects belonging to projects a user
+has edit permissions for. A user may not change the project of a
+subject.
+
+The *locations* array should have the mime-types of the subject's
+associated media. The response will contain signed s3 urls the client
+may make a PUT request containing the media to. The signed urls will
+be valid for 20 minutes.
+
+A request changing the *metadata* hash must contain a full
+representation of the attribute.
+
+## Destroy a single subject
+```http
+DELETE /api/subjects/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
+Users are permitted to destroy a subjects they own or
+subjects belongs to a project a user has destroy permissions for.
+
+## Subject Versions
+```json
+{
+ "meta": {
+ "versions": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/subjects/101/versions?page_size=2",
+ "previous_href": "/subjects/101/versions?page=14page_size=2",
+ "next_href": "/subjects/101/versions/?page=2&page_size=2",
+ "last_href": "/subjects/101/versions?page=14&page_size=2"
+ }
+ },
+ "versions": [{
+ "id": "42",
+ "changeset": {
+ "metadata": [{
+ "ra": "120.2",
+ "dec": "-12.4"
+ },{
+ "ra": "121.1",
+ "dec": "-11.1"
+ }]
+ },
+ "whodunnit": "stuartlynn",
+ "created_at": "2014-03-20T06:23:12Z",
+ "links": {
+ "item": {
+ "id": "101",
+ "href": "/subject/101",
+ "type": "subjects"
+ }
+ }
+ },{
+ "id": "43",
+ "changeset": {
+ "metadata": [{
+ "ra": "20.2",
+ "dec": "12.4"
+ },{
+ "ra": "21.1",
+ "dec": "11.1"
+ }]
+ },
+ "whodunnit": "stuartlynn",
+ "created_at": "2014-03-20T06:23:12Z",
+ "links": {
+ "item": {
+ "id": "101",
+ "href": "/subject/101",
+ "type": "subjects"
+ }
+ }
+ }]
+}
+```
+
+A Subject Version resource represents a set of changes made to
+a Subject resource.
+
+It has the following attributes:
+
+Attribute | Type | Description
+--------- | ---- | -----------
+id | integer | read-only
+changeset | jsonb | read-only
+whodunnit | string | read-only
+created_at | datetime | read-only
+
+
+**It is NOT editable.**
+
+## List all Subject Versions
+```http
+GET /api/subjects/123/versions HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + subject_id (required, integer) ... id of the subject to retreive versions for
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+
+Response will have a meta attribute hash containing paging
+information.
+
+Subject Versions are returned as an array under *versions*.
+
+
+## Retrieve a Single Subject Version
+```http
+GET /api/subjects/123/versions/2 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + subject_id (required, integer) ... id of the subject to retreive versions for
+ + id (required, integer) ... integer id of the version to load
+
+
+## Retrieve subjects to classify
+```http
+
+GET /api/v1/subjects/queued HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
+While the normal GET on the subjects resource will return a list of subjects in a project, there is a special API for getting some subjects that need classifications: `GET /api/v1/subjects/queued`. This special API is optimized specifically for serving a selection of subjects that should be shown to the user in the classify interface.
+
++ Parameters
+ + workflow_id (required, integer) ... filter to subjects belonging to a specific workflow
+ + subject_set_id (optional, integer) ... return subjects belonging to the identified subject_set, it is required when the workflow is grouped.
+
diff --git a/docs/source/includes/_user_groups.md b/docs/source/includes/_user_groups.md
new file mode 100644
index 000000000..f8339b214
--- /dev/null
+++ b/docs/source/includes/_user_groups.md
@@ -0,0 +1,242 @@
+# UserGroups
+```json
+{
+ "links": {
+ "user_groups.projects": {
+ "href": "/projects?owner={user_groups.owner_name}",
+ "type": "projects"
+ },
+ "user_groups.classifications": {
+ "href": "/classifications?user_group_id={user_groups.id}",
+ "type": "classifications"
+ },
+ "user_groups.collections": {
+ "href": "/collections?owner={user_groups.owner_name}"
+ "type": "collections"
+ },
+ "user_groups.users": {
+ "href": "/users?user_group_id={user_groups.id}",
+ "type": "users"
+ },
+ "user_groups.memberships": {
+ "href": "/memberships?user_group_id={user_groups.id}",
+ "type": "memberships"
+ }
+ },
+ "meta": {
+ "user_groups": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/user_groups?page_size=2",
+ "previous_href": "/user_groups?page=14page_size=2",
+ "next_href": "/user_groups?page=2&page_size=2",
+ "last_href": "/user_groups?page=14&page_size=2"
+ }
+ },
+ "user_groups": [{
+ "id": "42",
+ "name": "a_cool_group",
+ "display_name": "A Cool Group",
+ "owner_name": "a_cool_group",
+ "created_at": "2014-08-11T10:11:34Z",
+ "updated_at": "2014-12-11T00:11:34Z",
+ "classifications_count": "1002340",
+ "activated_state": "active",
+ "stats_visibility": "private_agg_only",
+ "links": {
+ "memberships": ["101", "102"],
+ "users": ["10001", "9102"],
+ "projects": ["10"],
+ "collections": ["11"]
+ }
+ },{
+ "id": "44",
+ "name": "a_cool_gang",
+ "display_name": "A Cool Gang",
+ "owner_name": "a_cool_gang",
+ "created_at": "2014-09-10T10:41:54Z",
+ "updated_at": "2014-11-11T01:21:33Z",
+ "classifications_count": "2341",
+ "activated_state": "active",
+ "stats_visibility": "public_show_all",
+ "links": {
+ "memberships": ["101", "102"],
+ "users": ["10001", "9102"],
+ "projects": ["10"],
+ "collections": ["11"]
+ }
+ }]
+}
+```
+
+A user group represents a collection of users that share ownership of
+projects, collections, and classifications. Individual users within
+the group can be given different levels of permissions to act on
+group owned resources.
+
+A User Group has the following attributes:
+
+Attribute | Type | Description
+--------- | ---- | -----------
+id | integer | read-only
+classifications_count | integer |
+activated_state | string |
+name | string |
+display_name | string |
+stats_visibility | string |
+created_at | datetime | read-only
+updated_at | datetime | read-only
+
+
+*id*, *created_at*, *updated_at*, and *classifications_count* are all
+ set by the API.
+
+## Stats Visibility Levels
+We have added Stats Visibility Levels for new stats features on User Groups. The `stats_visibility` is an enum type on the `user_group` model on Panoptes.
+
+**Currently there are 5 Levels of Visibility.**
+
+**Stats Visibility Levels (Matching number with its corresponding numeric in Panoptes):**
+
+0) `private_agg_only` (DEFAULT) : Only members of a user group can view aggregate stats. Individual stats are ONLY viewable by ADMINS of the user group.
+1) `private_show_agg_and_ind`: Only members of a user group can view aggregate stats. Individual stats is viewable by BOTH members and admins of the user group.
+2) `public_agg_only`: Anyone can view aggregate stats of the user group. Only ADMINS of the user group can view individual stats.
+3) `public_agg_show_ind_if_member`: Anyone can view aggregate stats of the user group. Both members and admins of the user group can view individual stats.
+4) `public_show_all`: Anyone can view aggregate stats of the user group and can view individual stats of the user group. Authentication/Authorization to view user_group stats is NOT needed.
+
+
+## List all User Groups
+```http
+GET /api/user_groups HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + page (optional, integer) ... index of the page to retrieve 1 by default
+ + page_size (optional, integer) ... number of items per page 20 by default
+ + sort (optional, string) ... field to sort by, id by default
+ + user_id (optional, integer) ... filter list to groups a user is part of
+ + include (optional, string) ... comma separated list of linked resources to include in the response
+
+Response has a meta attribute hash containing paging
+information.
+
+User Groups are returned as an array under *user_groups*.
+
+## Retrieve a single User Group
+```http
+GET /api/user_groups/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + id (required, integer) ... integer, id of the user group
+ + include (optional, string) ... comma separated list of linked resources to include in the response
+
+## Create a User Group
+```http
+POST /api/user_groups HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "user_groups": {
+ "display_name": "A Super Grouper!",
+ "links": {
+ "users": ["10", "22"]
+ }
+ }
+}
+```
+
+A user can create new group by just giving it a name (how it appears
+in a @mention and url) or display name (how it shown to other users).
+
+In the case where only a display name is provided, the name will be
+set to the underscored, downcased, and url escaped version of the
+display name. When only a name is provided, display_name will be set
+to the same string as name.
+
+Optionally links to other users who will be given
+memberships with the 'invited' state.
+
+
+## Edit a single User Group
+```http
+PUT /api/user_groups/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "user_groups": {
+ "display_name": "A Uncool Group",
+ "links": {
+ "projects": [],
+ "collections": []
+ }
+ }
+}
+```
+
+A user with edit permissions on a user group may edit the group's
+name, display_name, or links to projects, collections and
+users. Projects and Collections may only be removed. Removing a
+link to a project or collection will destroy the project or
+collection, removing a link to a user will set their
+membership state to inactive.
+
+Adding a user creates a membership link with an 'invited'
+state. Membership and Classification links cannot be modified.
+
+**This is NOT the recommended way to modify links.**
+
+## Destroy a User Group
+```http
+DELETE /api/user_groups/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
+A user may destroy a group if they have the requisite permissions. A
+destroyed group and linked projects, collections, and memberships will
+be placed in an 'inactive' state.
+
+
+## Add user links
+```http
+POST /user_groups/123/links/users HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "users": ["123", "23"]
+}
+```
+
+Only links to users may be added. Creates a membership for a user. The membership will be immediately
+added, but a user won't show up in the group's links until they set
+their membership to 'active'.
+Only links to users may be added.
+
+## Remove links
+```http
+DELETE /user_groups/123/links/users/1,2,3 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + id (required, integer) ... id of the group to be edited.
+ + link_type (required, string) ... name of the link to modify
+ + link_ids (required, string) ... comma separated list of ids to remove
+
+Allows links to users, projects, or collections to be removed. Removed
+projects and collections are deleted. Removed users have their
+membership set to 'inactive'.
diff --git a/docs/source/includes/_workflows.md b/docs/source/includes/_workflows.md
new file mode 100644
index 000000000..d5680f9e1
--- /dev/null
+++ b/docs/source/includes/_workflows.md
@@ -0,0 +1,487 @@
+# Workflows
+```json
+{
+ "links": {
+ "workflows.subjects": {
+ "href": "/subjects{?workflow_id=workflows.id}",
+ "type": "subjects"
+ },
+ "workflows.project": {
+ "href": "/projects/{workflows.project}",
+ "type": "projects"
+ },
+ "workflows.subject_sets": {
+ "href": "/subject_sets?workflow_id={workflows.id}",
+ "type": "subject_sets"
+ }
+ },
+ "meta": {
+ "workflows": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/workflows?page_size=2",
+ "previous_href": "/workflows?page=14page_size=2",
+ "next_href": "/workflows?page=2&page_size=2",
+ "last_href": "/workflows?page=14&page_size=2"
+ }
+},
+"workflows": [{
+ "id": "22",
+ "display_name": "Find moons",
+ "created_at": "2014-02-13T10:11:34Z",
+ "updated_at": "2014-02-13T10:11:34Z",
+ "classifications_count": 1000,
+ "pairwise": false,
+ "grouped" : false,
+ "prioritized": false,
+ "primary_language": "es_MX",
+ "workflow_version": "22.1",
+ "content_language": "en_US",
+ "first_task": "interest",
+ "tasks": {
+ "interest": {
+ "type": "drawing",
+ "question": "Color some points",
+ "tools": [
+ {"value": "red", "label": "Red", "type": "point", "color": "red"},
+ {"value": "green", "label": "Green", "type": "point", "color": "lime"},
+ {"value": "blue", "label": "Blue", "type": "point", "color": "blue"}
+ ],
+ "next": "shape"
+ },
+ "shape": {
+ "type": "multiple",
+ "question": "What shape is this galaxy?",
+ "answers": [
+ {"value": "smooth", "label": "Smooth"},
+ {"value": "features", "label": "Features"},
+ {"value": "other", "label": "Star or artifact"}
+ ],
+ "required": true,
+ "next": "roundness"
+ },
+ "roundness": {
+ "type": "single",
+ "question": "How round is it?",
+ "answers": [
+ {"value": "very", "label": "Very...", "next": "shape"},
+ {"value": "sorta", "label": "In between"},
+ {"value": "not", "label": "Cigar shaped"}
+ ],
+ "next": null
+ }
+ },
+ "links": {
+ "project": "1",
+ "subject_sets": ["10", "11", "12"]
+ }
+},{
+ "id": "23",
+ "display_name": "Find moons",
+ "created_at": "2014-02-13T10:11:34Z",
+ "updated_at": "2014-02-13T10:11:34Z",
+ "classifications_count": 1000,
+ "pairwise": false,
+ "grouped" : false,
+ "prioritized": false,
+ "primary_language": "es_MX",
+ "workflow_version": "22.1",
+ "content_language": "en_US",
+ "first_task": "interest",
+ "tasks": {
+ "interest": {
+ "type": "drawing",
+ "question": "Color some points",
+ "tools": [
+ {"value": "red", "label": "Red", "type": "point", "color": "red"},
+ {"value": "green", "label": "Green", "type": "point", "color": "lime"},
+ {"value": "blue", "label": "Blue", "type": "point", "color": "blue"}
+ ],
+ "next": "shape"
+ },
+ "shape": {
+ "type": "multiple",
+ "question": "What shape is this galaxy?",
+ "answers": [
+ {"value": "smooth", "label": "Smooth"},
+ {"value": "features", "label": "Features"},
+ {"value": "other", "label": "Star or artifact"}
+ ],
+ "required": true,
+ "next": "roundness"
+ },
+ "roundness": {
+ "type": "single",
+ "question": "How round is it?",
+ "answers": [
+ {"value": "very", "label": "Very...", "next": "shape"},
+ {"value": "sorta", "label": "In between"},
+ {"value": "not", "label": "Cigar shaped"}
+ ],
+ "next": null
+ }
+ },
+ "links": {
+ "project": "1",
+ "subject_sets": ["10", "11", "12"]
+ }
+}]
+}
+```
+
+Workflows represent the series of questions/tasks a user will be asked
+to complete for a subject. Subjects are selected from SubjectSets. A
+Workflow may have many SubjectSets linked to, but a SubjectSet may
+only be linked to a single Workflow.
+
+A workflow has the following attributes:
+
+Attribute | Type | Description
+--------- | ---- | -----------
+id | integer | read-only
+display_name | string |
+finished_at | datetime |
+tasks | jsonb |
+classifications_count | integer |
+pairwise | boolean |
+grouped | boolean |
+prioritized | boolean |
+retirement | jsonb |
+retired_set_member_subjects_count | integer |
+active | boolean |
+aggregation | jsonb |
+configuration | jsonb |
+completeness | decimal |
+primary_language | string |
+workflow_version | string |
+content_language | string |
+created_at | datetime | read-only
+updated_at | datetime | read-only
+
+
+*id*, *created_at*, *updated_at*, *workflow_version*, *content_language*,
+and *classifications_count* are assigned by the API
+
+*finished_at* is set by the API to a date/time when all subjects for this workflow have been retired.
+
+Three parameters: _grouped_, _prioritized_, and _pairwise_ configure
+how the api chooses subjects for classification. They are all false by default,
+which will give a random selection of subjects from all subject\_sets that
+are part of a workflow. _grouped_ enables selecting subjects from a specific
+subject set. _prioritized_ ensures that users will see subjects in a
+predetermined order. _pairwise_ will select two subjects at a tim for classification.
+
+A workflow's `tasks` is a hash of keys to task definitions.
+
+A workflow's `first_task` is a string matching the key of its first task. (The order of keys in JSON hashes is not guaranteed).
+
+Each task has a `type` string of "single" or "multiple" choice, or "drawing". (More task types to come, e.g. Serengeti-style filter and Sunspotter's comparison.)
+
+"multiple" and "drawing" tasks have a `next` string, linking to the next task in the workflow. If this string is empty, the workflow ends. In "single" tasks, each answer has a `next` string, allowing branching based on the user's decisions.
+
+"single" and "multiple" tasks have a `question` string, which the user must answer. Answers to the question are in an `answers` array. Each answer has a `label` string displayed to the user.
+
+"single" and "multiple" tasks may define a boolean `required`, which when true will force the user to provide an answer before moving on to the next task.
+
+"drawing" tasks have an `instruction` string telling the user how to complete the task.
+
+"drawing" tasks have a `tools` array.
+
+Each tool has a `label` shown to the user.
+
+Each tool has a string `type`. Options include:
+
++ point
++ ellipse
++ circle
++ line
++ rectangle
++ polygon
+
+Each tool has a string `color`, which is applied to the marks made by the tool. Any format valid as CSS can be used.
+
+## List All Workflows
+```http
+GET /api/workflows HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+ + sort (optional, string) ... field to sort by id by default
+ + project_id (optional, integer) ... filter workflows by project id
+ + include (optional, string) ... comma separated list of linked resources to load
+
+Response has a *meta* attribute hash containing
+paging information.
+
+Workflows are returned as an array under the _workflows_ key.
+
+## Retrieve a single Workflow
+```http
+GET /api/workflows/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + id (required, integer) ... id of the workflow
+ + include (optional, string) ... comma separated list of linked resources to include in the response
+
+## Create a Workflow
+```http
+POST /api/workflows HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "workflows": {
+ "display_name": "Spot Monsters!",
+ "tasks": {
+ "interest": {
+ "type": "drawing",
+ "question": "Color some points",
+ "tools": [
+ {"value": "red", "label": "Red", "type": "point", "color": "red"},
+ {"value": "green", "label": "Green", "type": "point", "color": "lime"},
+ {"value": "blue", "label": "Blue", "type": "point", "color": "blue"}
+ ],
+ "next": "shape"
+ },
+ "shape": {
+ "type": "multiple",
+ "question": "What shape is this galaxy?",
+ "answers": [
+ {"value": "smooth", "label": "Smooth"},
+ {"value": "features", "label": "Features"},
+ {"value": "other", "label": "Star or artifact"}
+ ],
+ "required": true,
+ "next": "roundness"
+ },
+ "roundness": {
+ "type": "single",
+ "question": "How round is it?",
+ "answers": [
+ {"value": "very", "label": "Very...", "next": "shape"},
+ {"value": "sorta", "label": "In between"},
+ {"value": "not", "label": "Cigar shaped"}
+ ],
+ "next": null
+ }
+ },
+ "retirement": {
+ "criteria": "classification_count",
+ "options": {
+ "count": 15
+ }
+ },
+ "primary_language": "en-ca",
+ "links": {
+ "project": "42",
+ "subject_sets": ["1", "2"]
+ }
+ }
+}
+```
+
+Requires a set of *tasks*, a *primary_language*, a *display_name*, and a
+link to a *project*. Can optionally set cellect parameters *grouped*,
+*prioritized*, and *pairwise* (all false by default) and links to
+*subject_sets*.
+
+A SubjectSet that already belongs to another workflow will be
+duplicated when it is linked.
+
+A Workflow may also include a _retirement_ object with a _criteria_
+key and an _options_ key. _criteria_ describes the strategy Panoptes
+will use to decide when to retire subjects while _options_ configures
+the strategy. There are 2 valid criteria:
+ 1. `classification_count` will retire subjects after a target number
+ of classifications are reached. You must supply an `options` hash
+ with an integer `count` to specify the minimum number of classifications.
+ + `{"criteria": "classification_count", "options": {"count": 15} }`
+ 2. `never_retire` will never retire subjects and requires an empty
+ `options` hash.
+ + `{"criteria": "never_retire "options": {} }`
+
+If retirement is left blank Panoptes defaults to the `classification_count`
+strategy with 15 classifications per subject.
+
+
+## Edit a single workflow
+```http
+PUT /api/workflows/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{
+ "workflows": {
+ "tasks": { "format": "all new!"},
+ "links": {
+ "subject_sets": ["8"]
+ }
+ }
+}
+```
+
+A user may edit a workflow if they have edit permissions for the parent
+project. Editing tasks content requires a full replacement for the
+field. Only the subject set link may be edited. Removing a subject_set
+link doesn't destroy the subject_set.
+
+This is not the recommended way to edit links. Use the subject_set
+link mode documented below.
+
+
+## Destroy a single workflow
+```http
+DELETE /api/workflows/123 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
+A user may destroy a workflow if they have destroy permissions for the
+parent project.
+
+## Link Subject Set to Workflow
+```http
+POST /api/workflows/123/links/subject_sets HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+
+{ "subject_sets": ["9"] }
+```
+
+**The recommended way to update links.**
+
+Adds the posted subject sets to a workflow's links. Creates a copy of
+the subject set if it belongs do a different project.
+
++ Parameters
+ + id (required, integer) ... id of workflow to update
+
+
+
+## Destroy Workflow's Subject Set Links
+```http
+DELETE /api/workflows/123/links/subject_sets/1,2,3 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
+**The recommended way to remove links.**
+
+Removes workflow's links to the given subject_sets. It does not
+destroy the subject set models.
+
++ Parameters
+ + id (required, integer) ... id of workflow to update
+ + subject_set_ids (required, string) ... comma separated list of ids to destroy
+
+## Workflow Versions
+```json
+{
+ "meta": {
+ "versions": {
+ "page": 1,
+ "page_size": 2,
+ "count": 28,
+ "include": [],
+ "page_count": 14,
+ "previous_page": 14,
+ "next_page": 2,
+ "first_href": "/workflows/101/versions?page_size=2",
+ "previous_href": "/workflows/101/versions?page=14page_size=2",
+ "next_href": "/workflows/101/versions/?page=2&page_size=2",
+ "last_href": "/workflows/101/versions?page=14&page_size=2"
+ }
+ },
+ "versions": [{
+ "id": "42",
+ "changeset": {
+ "grouped": [
+ true,
+ false
+ ]
+ },
+ "whodunnit": "stuartlynn",
+ "created_at": "2014-03-20T06:23:12Z",
+ "links": {
+ "item": {
+ "id": "101",
+ "href": "/workflows/101",
+ "type": "workflows"
+ }
+ }
+ },{
+ "id": "43",
+ "changeset": {
+ "prioritized": [
+ false,
+ true
+ ]
+ },
+ "whodunnit": "stuartlynn",
+ "created_at": "2014-03-20T06:23:12Z",
+ "links": {
+ "item": {
+ "id": "101",
+ "href": "/workflows/101",
+ "type": "workflows"
+ }
+ }
+ }]
+}
+```
+
+A Workflow Version resource represents a set of changes made to
+a Workflow resource.
+
+It has the following attributes:
+
+Attribute | Type | Description
+--------- | ---- | -----------
+id | integer | read-only
+changeset | hash | read-only
+whodunnit | string | read-only
+created_at | datetime | read-only
+
+
+**It is NOT editable.**
+
+## List All Workflow Versions
+```http
+GET /api/workflows/123/versions HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + workflow_id (required, integer) ... integer id of the workflow resource
+ + page (optional, integer) ... the index of the page to retrieve default is 1
+ + page_size (optional, integer) ... number of items to include on a page default is 20
+
+Response will have a meta attribute hash containing paging
+information.
+
+Workflow Versions are returned as an array under *versions*.
+
+## Retrieve a Single Version
+```http
+GET /api/workflows/123/versions/1 HTTP/1.1
+Accept: application/vnd.api+json; version=1
+Content-Type: application/json
+```
+
++ Parameters
+ + workflow_id (required, integer) ... integer id of the workflow resource
+ + id (required, integer) ... integer id of the version to retrieve
+
diff --git a/docs/source/index.html.md b/docs/source/index.html.md
index 283719102..ba85656a0 100644
--- a/docs/source/index.html.md
+++ b/docs/source/index.html.md
@@ -14,9 +14,24 @@ toc_footers:
includes:
- authentication
- json_api
+ - headers
+ - classifications
+ - collection_preferences
+ - collection_roles
+ - collections
+ - memberships
+ - organizations
+ - project_preferences
+ - project_roles
- projects
- subject_set_imports
+ - set_member_subjects
+ - subject_sets
+ - subject_workflow_statuses
+ - subjects
+ - user_groups
- users
+ - workflows
search: true
---