# Welcome

Developer tools for customized and automated access

#### Not a developer?

To learn more about Eduflow, visit our home page at <https://www.eduflow.com>. For usage, explanation, and LMS integration see our [help portal](https://help.eduflow.com). If you have a special case, feel free to reach out to us!

## Explore

Eduflow provides Institutions with API access to their organization's data. The API is organized around [GraphQL](https://www.graphql.org/) and structured against our data model. Access is scoped to information within your institution. From within, you can query data such as user information and course-related entities such as as activities, and submissions, reviews and scores.

{% content-ref url="/pages/-MRzy9ej55sIbQXjLSFJ" %}
[API Overview](/getting-started-1/overview)
{% endcontent-ref %}

{% content-ref url="/pages/-MT19MyKQxdVlLzfFRxR" %}
[GraphQL at Eduflow](/graphql/graphql)
{% endcontent-ref %}

## Who is developer access available to?

API Access is available to Pro and Premium institutional plans. To learn more, visit our [Pricing page](https://www.eduflow.com/pricing).&#x20;

## Getting in touch

Contact us via our [Support Email](mailto:support@eduflow.com) or [Help Portal](https://help.eduflow.com).


# API Overview

Convenient access to your institution's data

| Overview       |                                                                                              |
| -------------- | -------------------------------------------------------------------------------------------- |
| API Endpoint   | <https://app.eduflow.com/api/graphql>                                                        |
| Authentication | Authorization headers and session based for GraphiQL explorer                                |
| Scope          | Institution-wide: Institution data, courses, participants, activities, reviews, summary data |

{% hint style="info" %}
We're actively working on expanding our API to cover more use cases and be friendlier to use. To stay up to date, see our [changelog](/api-changelog).
{% endhint %}

## Learn GraphQL

[GraphQL](https://graphql.org/) is an API standard used across many websites (Facebook, GitHub). It's a big step from querying with REST. Take a dive into our GraphQL primer below.

{% content-ref url="/pages/-MSZMhX10JOvX-sqBCjG" %}
[Introduction to GraphQL](/graphql/graphql)
{% endcontent-ref %}

## Authenticate

Create an access key and learn how to authorize with the API.

{% content-ref url="/pages/-MSFG0eGQ527ETMz-zBC" %}
[Authentication](/getting-started-1/authentication)
{% endcontent-ref %}

## Usage

Try your first query with the Eduflow API.

{% content-ref url="/pages/-MSZSdRNuJ55VV0fsX9c" %}
[Usage](/getting-started-1/usage)
{% endcontent-ref %}

## Learn Eduflow's structure

Check out the object graph for an overview of how data is laid out in Eduflow.

* [Institution](/guides/institutions)
  * [Course](/guides/courses)
    * [Participants](/guides/courses#course-participants)
    * [Flows](/guides/flows-and-activities#flows)
      * [Activities ](/guides/flows-and-activities#activities)- submission, reviews, etc
        * [Scores ](/guides/course-summary-outputs)- feedback, submission, combined scores
        * [Submissions ](/guides/course-summary-outputs#submission)- pdf, xls, google drive, links, rich text

## Keeping up to date and sharing your ideas

Check out Eduflow's [Canny changelog](https://eduflow.canny.io/changelog) to see our latest activities. For upcoming changes, see our [Roadmap](https://eduflow.canny.io/) where you can vote on features and suggest features and enhancements to Eduflow and the Public API.

{% content-ref url="/pages/-MRzzLl\_lPTXiNcy59xF" %}
[Broken mention](broken://pages/-MRzzLl_lPTXiNcy59xF)
{% endcontent-ref %}


# Authentication

## API Keys

### What can I do with API Keys?

As of right now, this opens up access to our GraphQL API. Stay tuned to for more ways your API key can be used to integrate with third party services.

### How do I enable Developer settings for my institution?

Developer settings are available to Institutions with a [Pro level plan or greater](https://www.eduflow.com/pricing).&#x20;

From there, administrators and owners will be able to use their institution dashboard (accessible via top-left menu) to create keys.

![You can navigate to your Dashboard via the top left-menu](https://2518674650-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GmK_EkZyQQJhTJyLh%2F-MSZK6GVRpWJruepAfi3%2F-MSZKgq3W3GpcIjkLDf8%2Fimage.png?alt=media\&token=6c7aa58a-32d5-4009-85c4-5793869c47a1)

### How do I create an API access key?

API Keys are required to use the GraphQL API and other API Key related features can be created through the Developer API settings:

![From within the Institution Dashboard](https://2518674650-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GmK_EkZyQQJhTJyLh%2F-MSZK6GVRpWJruepAfi3%2F-MSZLERMj2sHvSpTpBZE%2Fimage.png?alt=media\&token=5ef63053-2b19-432c-834d-c999c4ded78f)

&#x20;And within the **Developer API** section:

![Clicking "Generate API Key" will produce a key to access your institution](https://2518674650-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GmK_EkZyQQJhTJyLh%2F-MSFCMmSDLUVcaEV6cRF%2F-MSFFi1FbLHA4zwwg8SQ%2Fdashboard-api-key-empty.png?alt=media\&token=76ea9e31-81d7-42a5-aa06-fa6ffbd536c6)

The institution's API key can be revoked, which will prevent future requests from accessing with that key.

If you would like a test / developer institution created for development, contact our support.

![](https://2518674650-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GmK_EkZyQQJhTJyLh%2F-MSFCMmSDLUVcaEV6cRF%2F-MSFFk3Md2Gje_0CD4R8%2Fdashboard-api-key-generated.png?alt=media\&token=2fb86e68-cb3b-4c31-8a80-bf9e7a29764b)

## Authentication methods

{% hint style="info" %}
For examples, see the [Usage page](/getting-started-1/usage).
{% endhint %}

### Bearer token (Authorization headers)

Authentication is passed via bearer token. This is done via taking your institution's [API key](/getting-started-1/authentication#api-keys) passing it in the [`Authorization` HTTP Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) in this format:

`Authorization: Bearer <api_key>`

#### Ways to pass authorization headers

* In browser, via an extension that modifies requests (e.g. [Postman](https://swagger.io/docs/specification/authentication/bearer-authentication/))
* Terminal usage: via `curl(1)` or `wget(1)` (see [examples](/getting-started-1/usage#api-access))
* Programmatically via network requests (see [examples](/getting-started-1/usage#api-access))

#### See also

* [HTTP Headers > Authorization](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) on MDN Web Docs
* Similar: [Swagger's Bearer authentication method](https://swagger.io/docs/specification/authentication/bearer-authentication/) ([archive.org](https://web.archive.org/web/20210202204128/https://swagger.io/docs/specification/authentication/bearer-authentication/))
* Similar: [Postman docks on Bearer authentication](https://learning.postman.com/docs/sending-requests/authorization/#bearer-token) ([archive.org](https://web.archive.org/web/20210126204525/https://learning.postman.com/docs/sending-requests/authorization/))
* Similar: [RFC6750 / OAuth 2.0 Authorization Framework: Bearer Token Usage](https://tools.ietf.org/html/rfc6750#section-2.1) ([archive.org](https://web.archive.org/web/20201214095310/https://tools.ietf.org/html/rfc6750))

### Via session

You can access <https://app.eduflow.com/api/graphql> and Login as an institution owner / admin to access graphiql directly. To see this in action see the [graphiql usage example](/getting-started-1/usage#session-based-graphiql).


# Usage

## API Access

|          |                                       |
| -------- | ------------------------------------- |
| Headers  | `Authorization: Bearer <api_key>`     |
| Endpoint | `https://app.eduflow.com/api/graphql` |

General requests to the API endpoint will return JSON.

The response will be wrapped in `"data"` and match the shape of the original query.

{% tabs %}
{% tab title="curl" %}
[curl(1)](https://curl.haxx.se/) example:

```javascript
EDUFLOW_API_KEY=<api_key> \
  curl \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer ${EDUFLOW_API_KEY}" \
  -X POST \
  -d '{"query": "{institution {name}}"}' \
  https://app.eduflow.com/api/graphql
```

{% endtab %}

{% tab title="wget" %}
[wget(1)](https://www.gnu.org/software/wget/) example:

```javascript
EDUFLOW_API_KEY=<api_key> \
 wget -qO- \
 --header 'Content-Type: application/json' \
 --header "Authorization: Bearer ${EDUFLOW_API_KEY}" \
 -X POST \
 --post-data '{"query": "{institution {name}}"}' \
 https://app.eduflow.com/api/graphql
```

{% endtab %}

{% tab title="node" %}

```javascript
#!/bin/env node

if (!process.env.EDUFLOW_API_KEY) {
  console.error(`Enter your EDUFLOW_API_KEY:

  env EDUFLOW_API_KEY=<api_key> node index.js`);
  return;
}

const http =
  process.env.EDUFLOW_API_PROTOCOL === "http"
    ? require("http")
    : require("https");

const query = JSON.stringify({
  query: `{
    institution {
      name
    }
  }`,
});

const options = {
  hostname: process.env.EDUFLOW_API_HOSTNAME || "app.eduflow.com",
  path: "/api/graphql",
  port: process.env.EDUFLOW_API_PORT || 443,
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Content-Length": query.length,
    Authorization: "Bearer " + process.env.EDUFLOW_API_KEY,
    "User-Agent": "Node",
  },
};

const req = http.request(options, (res) => {
  let data = "";
  res.on("data", (d) => {
    data += d;
  });
  res.on("end", () => {
    console.log(JSON.parse(data).data);
  });
});

req.on("error", (error) => {
  console.error(error);
});

req.write(query);
req.end();
```

{% endtab %}

{% tab title="python" %}

```python
#!/bin/env python
import json
import os
import sys
import urllib.request

api_key = os.getenv("EDUFLOW_API_KEY")
if api_key is None:
    sys.exit(
        """Enter your EDUFLOW_API_KEY:

    env EDUFLOW_API_KEY=<api_key> python app.py"""
    )

query = """{
  institution {
    name
  }
}"""

raw_data = {
    "query": query,
    "variables": {},
}
data = json.dumps(raw_data).encode("utf-8")

options = {
    "data": data,
    "headers": {
        "Content-Type": "application/json",
        "Content-Length": len(data),
        "Authorization": f"Bearer {api_key}",
        "User-Agent": "Node",
    },
}

hostname = os.getenv("EDUFLOW_API_HOSTNAME", "app.eduflow.com")
protocol = os.getenv("EDUFLOW_API_PROTOCOL", "https")
port = os.getenv("EDUFLOW_API_PORT", 443)
url = f"{protocol}://{hostname}:{port}/api/graphql"

request = urllib.request.Request(url, **options)

try:
    response = urllib.request.urlopen(request)
    print(response.read().decode())
except urllib.error.HTTPError as e:
    print(e.read().decode())
```

{% endtab %}
{% endtabs %}

Response:

```javascript
{"data":{"institution":{"name":"Test institution"}}}
```

## Session-based / GraphiQL

Institution administrators that are logged into Eduflow can access the API endpoint and query through the graphql interface.

When logged in, go directly to <https://app.eduflow.com/api/graphql>

![Hovering over an object will show an introspection tooltip, typing will show autocompletion hints](https://2518674650-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GmK_EkZyQQJhTJyLh%2F-MRzvVlW_aNdLdO5tFe_%2F-MRzvw-WLh7vAfolbVmN%2Fgraphiql-hover-and-completion.gif?alt=media\&token=bf55d3ad-d308-482f-bad4-b35e88a528ae)

![GraphiQL has a searchable documentation available on the sidebar](https://2518674650-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GmK_EkZyQQJhTJyLh%2F-MRzwW8qImzkVYf-zhN3%2F-MRzwZGQa36N6u66knEe%2Fgraphiql-docs.gif?alt=media\&token=4885fbb2-56b8-40c5-96b9-20830d024ad4)

If you access via browser, the GraphQL endpoint will show [GraphiQL](https://github.com/graphql/graphiql)

To open GraphiQL's sidebar, mouseover an object in the query and click an object inside a tooltip, or clicking "Doc":

![This is found on the top right of GraphiQL if the sidebar is closed.](https://2518674650-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GmK_EkZyQQJhTJyLh%2F-MSFIWCUT96ZXEROhs2w%2F-MSFJDpddkkKJ2YTqey1%2Fimage.png?alt=media\&token=ae2b70f6-96e0-4ce3-81b4-5e9cccd73891)

GraphiQL works with both session-based (logged into app.eduflow\.com) and by passing authorization headers.


# Introduction to GraphQL

## Query language

[GraphQL](https://www.graphql.org/) is a query language for API's. It's query language can pull multiple objects of different types, with less queries that would be needed using REST.

Where else is GraphQL used? Facebook, GitHub

#### What does GraphQL do?

REST API's follow a concept of [architectural constraints](https://en.wikipedia.org/wiki/Representational_state_transfer#Architectural_constraints). The classic API is multiple endpoints for various resources alongside `GET`, `POST,` `PUT` and `DELETE`.

GraphQL on the other hand usually has *one* endpoint, always `GET` or `POST`. The magic is in the query passed in the post data.

Assume the following:

```graphql
{
    institution {
        name
    }
}
```

Response:

```javascript
{
  "data": {
    "institution": {
      "name": "Happy University"
    }
  }
}
```

A couple things:

* The query to a single endpoint, via `GET` (`?query=...`) or `POST` (via `POST` data)
* The response is JSON
* The response structure matches the query

What's more is, GraphQL schemas enforce a structured, typed API, and provides instrumentation. This can best be put into example by seeing GraphiQL - a client for graphql - with the query above:

![](https://2518674650-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GmK_EkZyQQJhTJyLh%2F-MRzwe9S5r77_rrSuWij%2F-MRzwnCjnc1JBGbXtl1R%2Fgraphql-objects-and-types.gif?alt=media\&token=69a72706-25c3-445d-a2e5-a3ec9f8e3cda)

## Core concepts

#### Queries and Mutations:

* Query: Usually reading
* Mutation: Usually create, updating or deleting

#### Basics

* [Fields](https://graphql.org/learn/queries/#fields)
* [Type system](https://graphql.org/learn/schema/#object-types-and-fields):
  * [Object types](https://graphql.org/learn/schema/#object-types-and-fields)
  * [Enums](https://graphql.org/learn/schema/#the-query-and-mutation-types)
  * [Lists](https://graphql.org/learn/schema/#lists-and-non-null)
  * [Interfaces](https://graphql.org/learn/schema/#interfaces)

#### Advanced

* [Variables](https://graphql.org/learn/queries/#variables): Optional parametrization
* [Fragments](https://graphql.org/learn/queries/#fragments): Repeatable pieces of query
* [Pagination](https://graphql.org/learn/pagination/): Traverse through lists of data


# Variables

## Variables

GraphQL offers a way to reuse your queries dynamically through [variables](https://graphql.org/learn/queries/#variables). These will come into play especially if you're accessing the API programmatically (such as our [Node or Python examples](/getting-started-1/usage#api-access)).

Before:

{% tabs %}
{% tab title="Query (no variables)" %}

```graphql
{
  course(id: "00000000-5945-95c7-65fd-a9747b200317") {
    id
    title
    flows {
      title
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "course": {
      "id": "00000000-5945-95c7-65fd-a9747b200317",
      "title": "History of Dunder-Mifflin",
      "flows": [
        {
          "title": "Founding a Company"
        },
        {
          "title": "Corporate Citizenship"
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}

With variables:

{% tabs %}
{% tab title="Query (with variables)" %}

```graphql
query($courseId: UUID!) {
  course(id: $courseId) {
    id
    title
    flows {
      title
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "course": {
      "id": "00000000-5945-95c7-65fd-a9747b200317",
      "title": "History of Dunder-Mifflin",
      "flows": [
        {
          "title": "Founding A Company"
        },
        {
          "title": "Corporate Citizenship"
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}

Here's an example of what this would look like [in the API Explorer](/getting-started-1/usage#session-based-graphiql):

![GraphiQL allows variables to be passed on the bottom-left pane.](https://2518674650-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GmK_EkZyQQJhTJyLh%2F-MScskjCNqzXIW35iIFf%2F-MSd2LcjhVG898I8xEgY%2Fimage.png?alt=media\&token=03811052-4c9c-4ddb-aa6b-b32b1e1ec7d7)

#### See also

* [Working with variables](https://docs.github.com/en/graphql/guides/forming-calls-with-graphql#working-with-variables) from GitHub's GraphQL API docs ([archive.org](https://web.archive.org/web/20210203142000/https://docs.github.com/en/graphql/guides/forming-calls-with-graphql#working-with-variables))
* [GraphQL Variables](https://shopify.dev/concepts/graphql/variables) from Shopify's API docs ([archive.org](https://web.archive.org/web/20200420144945/https://shopify.dev/concepts/graphql/variables))

##

##


# Fragments

Reuse fields for the same object type

You can also reuse chunks of your queries with [GraphQL fragments](https://graphql.org/learn/queries/#fragments):

{% tabs %}
{% tab title="With fragments" %}

```graphql
fragment ReuseableFragment on Course {
  id
  title
  flows {
    title
  }
}

{
  course(id: "00000000-5945-95c7-65fc-a9747b20031d") {
    ...ReuseableFragment
  }
  anotherCourse: course(id: "00000000-5945-95c6-65fc-a9747b200320") {
    ...ReuseableFragment
  }
}
```

{% endtab %}

{% tab title="Without fragments" %}

```graphql
{
  course(id: "00000000-5945-95c7-65fc-a9747b20031d") {
    id
    title
    flows {
      title
    }
  }
  anotherCourse: course(id: "00000000-5945-95c6-65fc-a9747b200320") {
    id
    title
    flows {
      title
    }
  }
}
```

{% endtab %}

{% tab title="Results" %}

```javascript
{
  "data": {
    "course": {
      "id": "00000000-5945-95c6-65fc-a9747b200317",
      "title": "History 101",
      "flows": [
        {
          "title": "Welcome to History 101"
        },
        {
          "title": "Nascent Civilization"
        }
      ]
    },
    "anotherCourse": {
      "id": "00000000-5945-95c7-65fd-a9747b200320",
      "title": "M & A 101",
      "flows": [
        {
          "title": "Welcome to Mergers & Acquisitions 101"
        },
        {
          "title": "Flow 1 - SEC Regulatory Measures"
        },
        {
          "title": "Flow 2 - Intro"
        },
        {
          "title": "Flow 3 - Mergers"
        },
        {
          "title": "Flow 4 - Acquistions"
        },
        {
          "title": "Mergeable activities"
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}

Another thing to point out: `anotherCourse` is a [GraphQL alias](https://graphql.org/learn/queries/#aliases). This is a way to make multiple queries to the same field (`query.course`)


# Paginating queries

The Eduflow API supports paginating lists of data through standard [GraphQL connections](https://graphql.org/learn/pagination/#end-of-list-counts-and-connections).

### Example

{% tabs %}
{% tab title="Page 1" %}
`first: 2` will pick the first 2 reviews from this Teacher review activity:

```graphql
{
  activity(id: "5572112e-66ad-45de-bd83-1332df79f51c") {
    ... on TeacherReviewActivity {
      title
      activityType
      reviews(first: 2) {
        pageInfo {
          hasPreviousPage
          hasNextPage
          endCursor
        }
        edges {
          node {
            createdAt
            modifiedAt
            answers {
              booleanAnswer
              scaleAnswer
              textAnswer
              comment
              scaleAnswerPercentage
            }
            givers {
              email
            }
            receivers {
              email
            }
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}
Response includes requested data and `endCursor` of `YXJyYXljb25uZWN0aW9uOjE=`, which we'll use in Page 2:

```javascript
{
  "data": {
    "activity": {
      "title": "Flow 1 - Activity 5 - Teacher's Perspective",
      "activityType": "TeacherReview",
      "reviews": {
        "pageInfo": {
          "hasPreviousPage": false,
          "hasNextPage": true,
          "endCursor": "YXJyYXljb25uZWN0aW9uOjE="
        },
        "edges": [
          {
            "node": {
              "createdAt": "2021-02-03T07:06:20.054270",
              "modifiedAt": "2021-02-03T07:06:20.054275",
              "answers": [
                {
                  "booleanAnswer": null,
                  "scaleAnswer": null,
                  "textAnswer": "Some awesome answer for question \"Question of type Text\" (a17d3ff4-25ac-4f15-bfde-8422112cf072) However media expert mind everything key shake manage clear until yes much realize professor defense begin national to authority among government seek performance probably compare role something image Congress next tree receive western wait bed memory dark person boy population size whom road they none spring information husband health officer past marriage officer where one success idea contain top become he case eight cell through rich late manager data every economic increase billion central almost.",
                  "comment": null,
                  "scaleAnswerPercentage": null
                },
                {
                  "booleanAnswer": null,
                  "scaleAnswer": 1,
                  "textAnswer": null,
                  "comment": null,
                  "scaleAnswerPercentage": 25
                },
                {
                  "booleanAnswer": false,
                  "scaleAnswer": null,
                  "textAnswer": null,
                  "comment": "Yard consumer subject plant every than education life.",
                  "scaleAnswerPercentage": null
                }
              ],
              "givers": [
                {
                  "email": "Geoff@example.edu"
                }
              ],
              "receivers": [
                {
                  "email": null
                }
              ]
            }
          },
          {
            "node": {
              "createdAt": "2021-02-03T00:06:20.095326",
              "modifiedAt": "2021-02-03T00:06:20.095335",
              "answers": [
                {
                  "booleanAnswer": null,
                  "scaleAnswer": null,
                  "textAnswer": "Some awesome answer for question \"Question of type Text\" (a17d3ff4-25ac-4f15-bfde-8422112cf072) Author easy herself treat budget majority recently theory onto argue foreign win situation shake sense certain opportunity coach picture family picture attack almost short should discover allow down while picture finish every tree watch across above consumer audience after building environmental look second identify live challenge edge establish why lawyer that white customer market idea our local when rich television still indeed seek investment ever attorney analysis why about major everybody culture expect what wonder.",
                  "comment": null,
                  "scaleAnswerPercentage": null
                },
                {
                  "booleanAnswer": null,
                  "scaleAnswer": 4,
                  "textAnswer": null,
                  "comment": null,
                  "scaleAnswerPercentage": 100
                },
                {
                  "booleanAnswer": true,
                  "scaleAnswer": null,
                  "textAnswer": null,
                  "comment": "In including wonder policy some piece scientist.",
                  "scaleAnswerPercentage": null
                }
              ],
              "givers": [
                {
                  "email": "Geoff@example.edu"
                }
              ],
              "receivers": [
                {
                  "email": null
                }
              ]
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}

{% tab title="Page 2" %}
Pass the first page's `endCursor` into `first`:

```graphql
{
  activity(id: "5572112e-66ad-45de-bd83-1332df79f51c") {
    ... on TeacherReviewActivity {
      title
      activityType
      reviews(first: 2, after: "YXJyYXljb25uZWN0aW9uOjE=") {
        pageInfo {
          hasPreviousPage
          hasNextPage
          endCursor
        }
        edges {
          node {
            createdAt
            modifiedAt
            answers {
              booleanAnswer
              scaleAnswer
              textAnswer
              comment
              scaleAnswerPercentage
            }
            givers {
              email
            }
            receivers {
              email
            }
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}

* Begins directly on the second page
* `hasPreviousPage` is now `true`
* New `endCursor` is `YXJyYXljb25uZWN0aW9uOjM=`, let's use it on Page 3:

```javascript
{
  "data": {
    "activity": {
      "title": "Flow 1 - Activity 5 - Teacher's Perspective",
      "activityType": "TeacherReview",
      "reviews": {
        "pageInfo": {
          "hasPreviousPage": true,
          "hasNextPage": true,
          "endCursor": "YXJyYXljb25uZWN0aW9uOjM="
        },
        "edges": [
          {
            "node": {
              "createdAt": "2021-02-03T09:12:20.137659",
              "modifiedAt": "2021-02-03T09:12:20.137664",
              "answers": [
                {
                  "booleanAnswer": null,
                  "scaleAnswer": null,
                  "textAnswer": "Some awesome answer for question \"Question of type Text\" (a17d3ff4-25ac-4f15-bfde-8422112cf072) Be station board find own rest either agreement account report investment personal form information fall rise guess site local father attorney season argue call along Congress crime watch again watch decide method mean leader because plan decision audience tonight kitchen by box carry serve we look who realize focus check create newspaper next room either event every business box section cup feel father direction college difficult garden computer production should side decision force issue send challenge return debate force production half lawyer walk inside positive that beautiful allow response land.",
                  "comment": null,
                  "scaleAnswerPercentage": null
                },
                {
                  "booleanAnswer": null,
                  "scaleAnswer": 4,
                  "textAnswer": null,
                  "comment": null,
                  "scaleAnswerPercentage": 100
                },
                {
                  "booleanAnswer": true,
                  "scaleAnswer": null,
                  "textAnswer": null,
                  "comment": "Reflect leader understand another management.",
                  "scaleAnswerPercentage": null
                }
              ],
              "givers": [
                {
                  "email": "Murphy@example.edu"
                }
              ],
              "receivers": [
                {
                  "email": null
                }
              ]
            }
          },
          {
            "node": {
              "createdAt": "2021-02-03T00:06:20.179206",
              "modifiedAt": "2021-02-03T00:06:20.179212",
              "answers": [
                {
                  "booleanAnswer": null,
                  "scaleAnswer": null,
                  "textAnswer": "Some awesome answer for question \"Question of type Text\" (a17d3ff4-25ac-4f15-bfde-8422112cf072) Feel artist star determine family trouble man enjoy turn medical list mouth itself together sense along than authority bag skin check simply heart assume issue by office though community read trial since success you TV worry your stuff security office provide oil push treatment area these early discussion even treatment general hope whole enjoy financial talk after health kitchen ready region area support meet next consumer federal street section traditional him garden crime anything ready thought western sometimes line chance nor anything can thing sense before available view us both attack lay show information form wind memory main program center rock value sense personal.",
                  "comment": null,
                  "scaleAnswerPercentage": null
                },
                {
                  "booleanAnswer": null,
                  "scaleAnswer": 4,
                  "textAnswer": null,
                  "comment": null,
                  "scaleAnswerPercentage": 100
                },
                {
                  "booleanAnswer": false,
                  "scaleAnswer": null,
                  "textAnswer": null,
                  "comment": "Five economic affect writer approach white.",
                  "scaleAnswerPercentage": null
                }
              ],
              "givers": [
                {
                  "email": "murphy@example.com"
                }
              ],
              "receivers": [
                {
                  "email": null
                }
              ]
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}

{% tab title="Page 3" %}
This time, for variety, let's just take `1`, and return different fields:

```graphql
{
  activity(id: "5572112e-66ad-45de-bd83-1332df79f51c") {
    ... on TeacherReviewActivity {
      title
      activityType
      reviews(first: 1, after: "YXJyYXljb25uZWN0aW9uOjM=") {
        pageInfo {
          endCursor
        }
        edges {
          node {
            reviewType
            createdAt
            modifiedAt
            answers {
              scaleAnswerPercentage
            }
            givers {
              email
            }
            receivers {
              email
            }
          }
        }
      }
    }
  }
}

```

Based on the query being adjust to `first: 1`, we should only see one item returned in page 3.
{% endtab %}

{% tab title="Response" %}

* Only shows the fields requested (fields don't need to be the same each time)
* Shows `reviewType`
* Only shows 1 result, per `first`

```javascript
{
  "data": {
    "activity": {
      "title": "Flow 1 - Activity 5 - Teacher's Perspective",
      "activityType": "TeacherReview",
      "reviews": {
        "pageInfo": {
          "endCursor": "YXJyYXljb25uZWN0aW9uOjQ="
        },
        "edges": [
          {
            "node": {
              "reviewType": "TeacherReview",
              "createdAt": "2021-02-03T09:36:20.221437",
              "modifiedAt": "2021-02-03T09:36:20.221441",
              "answers": [
                {
                  "scaleAnswerPercentage": null
                },
                {
                  "scaleAnswerPercentage": 125
                },
                {
                  "scaleAnswerPercentage": null
                }
              ],
              "givers": [
                {
                  "email": "murphy@example.edu"
                }
              ],
              "receivers": [
                {
                  "email": "michael@example.edu"
                }
              ]
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

The above example moves between 3 pages, passing the `endCursor` of the previous query into the `after` of the next page's.  The initial (first) page doesn't need any `after`. The final page alters the fields queried and changes the amount returned.

#### Bonus: Start at `last` item and travel backwards

{% tabs %}
{% tab title="Page 1" %}
`last: 2` instead of `first`:

```graphql
{
  activity(id: "5572112e-66ad-45de-bd83-1332df79f51c") {
    ... on TeacherReviewActivity {
      title
      activityType
      reviews(last: 2) {
        pageInfo {
          hasPreviousPage
          hasNextPage
          startCursor
        }
        edges {
          node {
            reviewType
            createdAt
            modifiedAt
            answers {
              scaleAnswerPercentage
            }
            givers {
              email
            }
            receivers {
              email
            }
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}

* Returns 2 items
* Last item
* `hasPreviousPage` is `true`
* `startCursor` of `YXJyYXljb25uZWN0aW9uOjQ=` will be used in `before` on the next page

```javascript
{
  "data": {
    "activity": {
      "title": "Flow 1 - Activity 5 - Teacher's Perspective",
      "activityType": "TeacherReview",
      "reviews": {
        "pageInfo": {
          "hasPreviousPage": true,
          "startCursor": "YXJyYXljb25uZWN0aW9uOjQ="
        },
        "edges": [
          {
            "node": {
              "reviewType": "TeacherReview",
              "createdAt": "2021-02-03T08:06:20.221437",
              "modifiedAt": "2021-02-03T08:06:20.221441",
              "answers": [
                {
                  "scaleAnswerPercentage": null
                },
                {
                  "scaleAnswerPercentage": 125
                },
                {
                  "scaleAnswerPercentage": null
                }
              ],
              "givers": [
                {
                  "email": "nick@example.edu"
                }
              ],
              "receivers": [
                {
                  "email": "michael@example.edu"
                }
              ]
            }
          },
          {
            "node": {
              "reviewType": "TeacherReview",
              "createdAt": "2021-02-03T08:06:20.261409",
              "modifiedAt": "2021-02-03T08:06:20.261414",
              "answers": [
                {
                  "scaleAnswerPercentage": null
                },
                {
                  "scaleAnswerPercentage": 125
                },
                {
                  "scaleAnswerPercentage": null
                }
              ],
              "givers": [
                {
                  "email": "nick@example.edu"
                }
              ],
              "receivers": [
                {
                  "email": null
                }
              ]
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}

{% tab title="Page 2" %}
Since we're moving backwards, we use `before`:

```graphql
{
  activity(id: "5572112e-66ad-45de-bd83-1332df79f51c") {
    ... on TeacherReviewActivity {
      title
      activityType
      reviews(last: 2, before: "YXJyYXljb25uZWN0aW9uOjU=") {
        pageInfo {
          hasPreviousPage
          startCursor
        }
        edges {
          node {
            reviewType
            createdAt
            modifiedAt
            answers {
              scaleAnswerPercentage
            }
            givers {
              email
            }
            receivers {
              email
            }
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}
Continue with cursor

```javascript
{
  "data": {
    "activity": {
      "title": "Flow 1 - Activity 5 - Teacher's Perspective",
      "activityType": "TeacherReview",
      "reviews": {
        "pageInfo": {
          "hasPreviousPage": true,
          "startCursor": "YXJyYXljb25uZWN0aW9uOjM="
        },
        "edges": [
          {
            "node": {
              "reviewType": "TeacherReview",
              "createdAt": "2021-02-03T07:06:20.179206",
              "modifiedAt": "2021-02-03T07:06:20.179212",
              "answers": [
                {
                  "scaleAnswerPercentage": null
                },
                {
                  "scaleAnswerPercentage": 100
                },
                {
                  "scaleAnswerPercentage": null
                }
              ],
              "givers": [
                {
                  "email": "geoff@example.edu"
                }
              ],
              "receivers": [
                {
                  "email": null
                }
              ]
            }
          },
          {
            "node": {
              "reviewType": "TeacherReview",
              "createdAt": "2021-02-03T07:06:20.221437",
              "modifiedAt": "2021-02-03T07:06:20.221441",
              "answers": [
                {
                  "scaleAnswerPercentage": null
                },
                {
                  "scaleAnswerPercentage": 125
                },
                {
                  "scaleAnswerPercentage": null
                }
              ],
              "givers": [
                {
                  "email": "geoff@example.edu"
                }
              ],
              "receivers": [
                {
                  "email": "nicholas@example.edu"
                }
              ]
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

### GraphQL Terms

| Term       | Description                                                                                                                                                                                                                                                                                       |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `first`    | For slicing, fetch the items at the beginning                                                                                                                                                                                                                                                     |
| `last`     | For slicing, beginning at last item                                                                                                                                                                                                                                                               |
| `after`    | Accepts a cursor ID of where to continue paginating                                                                                                                                                                                                                                               |
| `cursor`   | ID of the cursor, in Eduflow, sometimes equal to `pageInfo.endCursor`                                                                                                                                                                                                                             |
| `pageInfo` | <p>Structure containing cursor location, item count, previous/next page</p><ul><li><code>startCursor</code></li><li><code>endCursor</code>: Use this in the next page's <code>after:</code> to start on next page</li><li><code>hasPreviousPage</code></li><li><code>hasNextPage</code></li></ul> |
| `edges`    | List of containing `node`s (list of objects) of the connection                                                                                                                                                                                                                                    |
| `node`     | The object itself, e.g. `User`, `Activity`                                                                                                                                                                                                                                                        |

#### See also

* [GraphQL Cursor Connections Specification](https://relay.dev/graphql/connections.htm) on relay.dev ([archive.org](https://web.archive.org/web/20200423070245/https://relay.dev/graphql/connections.htm))
* [Pagination](https://graphql.org/learn/pagination/) on graphql.org ([archive.org](https://web.archive.org/web/20210110104322/https://graphql.org/learn/pagination/))


# Inline fragments

GraphQL's permits [polymorphism](https://en.wikipedia.org/wiki/Polymorphism_\(computer_science\)) through [inline fragments](https://graphql.org/learn/queries/#inline-fragments), through objects being available as[ GraphQL Unions](https://graphql.org/learn/schema/#union-types) and [Interfaces](https://graphql.org/learn/schema/#interfaces). Eduflow uses inline fragments across the various [activity object types](/guides/flows-and-activities#activities).

Eduflow activities all have `Activity.title`, so all activities inherit from `ActivityInterface`.

### Querying

{% tabs %}
{% tab title="Query" %}

```graphql
{
  course(id: "00000000-5945-95c7-65fc-a9747b200320") {
    flows {
      activities {
        ... on ActivityInterface {
          title
        }
      }
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "course": {
      "flows": [
        {
          "activities": [
            {
              "title": "Welcome to the root-level Activity"
            }
          ]
        },
        {
          "activities": [
            {
              "title": "Learn about paper"
            },
            {
              "title": "Environmental friendliness"
            },
            {
              "title": "Climate change"
            }
          ]
        },
        {
          "activities": [
            {
              "title": "Introduction to sales"
            },
            {
              "title": "CRM technology"
            },
            {
              "title": "Funnel configuration"
            }
          ]
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Inline fragments

Using specific activity fragments act as a way to [upcast ](https://en.wikipedia.org/wiki/Type_conversion)objects. Here's an example of `PeerReview` ran against the connection above, and via direct activity lookup.

{% tabs %}
{% tab title="Query (Connection)" %}

```graphql
{
  course(id: "00000000-5945-95c6-65fc-a9747b200320") {
    flows {
      activities {
        __typename
        ... on ActivityInterface {
          title
        }
        ... on PeerReviewActivity {
          reviews {
            edges {
              node {
                reviewType
                createdAt
              }
            }
          }
        }
      }
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}
Notice the specialized fields only return for the activity type that are `PeerReview`s:

```javascript
{
  "data": {
    "course": {
      "flows": [
        {
          "activities": [
            {
              "__typename": "ContentActivity",
              "title": "Welcome aboard!"
            }
          ]
        },
        {
          "activities": [
            {
              "__typename": "ContentActivity",
              "title": "Our Mission"
            },
            {
              "__typename": "SubmissionActivity",
              "title": "Make an impact"
            },
            {
              "__typename": "PeerReviewActivity",
              "title": "Spot-check and team build",
              "reviews": {
                "edges": [
                  {
                    "node": {
                      "reviewType": "PeerReview",
                      "createdAt": "2021-02-08T17:56:30.576582"
                    }
                  },
                  {
                    "node": {
                      "reviewType": "PeerReview",
                      "createdAt": "2021-02-08T17:56:30.617394"
                    }
                  },
                  {
                    "node": {
                      "reviewType": "PeerReview",
                      "createdAt": "2021-02-08T17:56:30.661450"
                    }
                  },
                  {
                    "node": {
                      "reviewType": "PeerReview",
                      "createdAt": "2021-02-08T17:56:30.703841"
                    }
                  },
                  {
                    "node": {
                      "reviewType": "PeerReview",
                      "createdAt": "2021-02-08T17:56:32.501897"
                    }
                  }
                ]
              }
            },
            {
              "__typename": "FeedbackReflectionActivity",
              "title": "Introspect and reflect"
            },
            {
              "__typename": "TeacherReviewActivity",
              "title": "From your manager"
            },
            {
              "__typename": "SelfReviewActivity",
              "title": "How would you improve?"
            },
            {
              "__typename": "ContentActivity",
              "title": "Water cooler"
            },
            {
              "__typename": "ContentActivity",
              "title": "Networking: Meet across divisions"
            }
          ]
        },
        {
          "activities": [
            {
              "__typename": "ContentActivity",
              "title": "Paper: Relevant today?"
            },
            {
              "__typename": "SubmissionActivity",
              "title": "How do you use paper?"
            },
            {
              "__typename": "ContentActivity",
              "title": "Our philosophy"
            }
          ]
        }
      ]
    }
  }
}
```

{% endtab %}

{% tab title="Query (Direct lookup)" %}
`title` exists in `PeerReviewActivity` as well, no need to use `ActivityInterface`:

```graphql
{
  activity(id: "10aa7684-f0b2-4595-9161-4845e3602312") {
    ... on PeerReviewActivity {
      title
      reviews {
        edges {
          node {
            reviewType
            createdAt
          }
        }
      }
    }
  }
}
```

![title exists on PeerReviewActivity, as it inherits from ActivityInterface](https://2518674650-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GmK_EkZyQQJhTJyLh%2F-MT15rSgaF_WktWe1E4F%2F-MT16Ggg_C1Dyyudl0f1%2Fimage.png?alt=media\&token=a5058c66-4387-4ab8-85eb-ec63d1d248f6)
{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "activity": {
      "title": "Spot-check and team build",
      "reviews": {
        "edges": [
          {
            "node": {
              "reviewType": "PeerReview",
              "createdAt": "2021-02-08T17:56:30.576582"
            }
          },
          {
            "node": {
              "reviewType": "PeerReview",
              "createdAt": "2021-02-08T17:56:30.617394"
            }
          },
          {
            "node": {
              "reviewType": "PeerReview",
              "createdAt": "2021-02-08T17:56:30.661450"
            }
          },
          {
            "node": {
              "reviewType": "PeerReview",
              "createdAt": "2021-02-08T17:56:30.703841"
            }
          },
          {
            "node": {
              "reviewType": "PeerReview",
              "createdAt": "2021-02-08T17:56:32.501897"
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

### See also

* [Inline fragments](https://graphql.org/learn/queries/#inline-fragments) on graphql.org
* [Unions](https://graphql.org/learn/schema/#union-types) on on graphql.org
* [Interfaces](https://graphql.org/learn/schema/#interfaces) on graphql.org


# Institutions

Eduflow's Public API is built around your institution. Institutions in Eduflow are the same concept as organizations in API's like GitHub and Sentry.

Institution membership is managed through [participants](/guides/participants). Institution owners can invite to them (or automatically have them linked upon registration through trusted domain names)

![Search, sort, and invite members to your Institution ](https://2518674650-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GmK_EkZyQQJhTJyLh%2F-MSdyzuTPNfwvF3xp8yJ%2F-MSdzyU5oubilxSqRfbU%2Fimage.png?alt=media\&token=b2d095bf-899c-4a82-a1d6-43adc8c0e3b1)

Institutions own [courses](/guides/courses), which are created by their instructors.

![Institution dashboard: Course list (and search functionality)](https://2518674650-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GmK_EkZyQQJhTJyLh%2F-MSdyzuTPNfwvF3xp8yJ%2F-MSe-4n_Go8gmlbnQDXV%2Fimage.png?alt=media\&token=03c12f2d-e3b1-4fc4-9143-69f182784eba)

### Querying

Automatically scoped to your institutio

{% tabs %}
{% tab title="Query" %}

```graphql
{
  institution {
    createdAt
    name
    owner {
      name
      username
      email
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "institution": {
      "createdAt": "2021-02-08T17:39:53.137480",
      "name": "Duckburg University",
      "owner": {
        "name": "Louanne Kelley",
        "username": "louanne",
        "email": "kelley@dunder-mifflin.com"
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}


# Courses

Instructors can create courses. When an instructor creates a course, the course is associated to the instructor's institution.

Courses always have exactly one [institution](/guides/institutions).

#### Course participants

Course [Participants](/guides/participants) are a roster of instructors and students in a course. When an instructor adds them through the course's s participants page, they will be a course participant. If they're not already an Institution participant, they'll also be linked to the institution the course belongs to.

Participants can be one type in a course: `Owner`, `Instructor`, `Student`, or `Assistant`

#### Invite links

The invite link for learners can be [configured through the course participants page](https://help.eduflow.com/en/articles/4237088-inviting-learners-to-your-course#:~:text=in%20different%20ways.-,By%20sharing%20a%20link,-Share%20the%20course). In the example below they are queryable via `joinUrl`.

### Querying

#### Listing (inside institution)

{% tabs %}
{% tab title="Query" %}

```graphql
{
  institution {
    courses {
      edges {
        node {
          id
          title
          joinUrl
          flows {
            title
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "institution": {
      "courses": {
        "edges": [
          {
            "node": {
              "id": "00000000-5945-95c7-65fc-a9747b200320",
              "title": "Onboarding (New Employees)",
              "joinUrl": "https://app.eduflow.com/join/ZM2MNV",
              "flows": [
                {
                  "title": "Root flow"
                },
                {
                  "title": "Corporate Culture"
                },
                {
                  "title": "Paper in the 2000's"
                }
              ]
            }
          },
          {
            "node": {
              "id": "00000000-5945-95c7-65fc-a9747b200317",
              "title": "History of Dunder-Mifflin",
              "joinUrl": "https://app.eduflow.com/course/9813-onboarding-new-employeees",
              "flows": [
                {
                  "title": "Welcome to History of Dunder-Mifflin"
                },
                {
                  "title": "Founding a Company"
                }
              ]
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

#### via ID

{% tabs %}
{% tab title="Query" %}

```graphql
{
  course(id: "00000000-5945-95c7-65fc-a9747b300320") {
    id
    title
    joinUrl
    flows {
      title
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "course": {
      "id": "00000000-5945-95c7-65fc-a9747b200320",
      "title": "Onboarding (New Employees)",
      "joinUrl": "https://app.eduflow.com/join/ZM2MNV",
      "flows": [
        {
          "title": "Root flow"
        },
        {
          "title": "Corporate Culture"
        },
        {
          "title": "Paper in the 2000's"
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Mutations

#### Inviting participants

You can invite Instructors and Students (learners) via `addCourseParticipants`

{% tabs %}
{% tab title="Query" %}

```graphql
mutation {
  addCourseParticipants(
    courseId: "00000000-5945-95c7-65fc-a9747b200311", 
    inviteNow: true, 
    invitees: [
      {name: "Thomas Smith", email: "sthomas@youruniversity.edu"}
    ],
    participantType: Student
  ) {
    newParticipants {
      id
      participantType
      participantStatus
      user {
        name
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}

```json
{
  "data": {
    "addCourseParticipants": {
      "newParticipants": [
        {
          "id": "UGFydGljaXBhbnQ6OTYyOGYwNGYtNjMyZi00YTZhLWEwMTItMjdkZWZlZTMwNTIv",
          "participantType": "Student",
          "participantStatus": "INVITATION_PENDING",
          "user": {
            "name": "Thomas Smith"
          }
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}

#### Invite participants: Invite links

Invitation links and embed (a.k.a magic links) can be accessed through the `Participant` response object:

{% tabs %}
{% tab title="Query" %}

```graphql
mutation {
  addCourseParticipants(
    courseId: "00000000-5945-95c7-65fc-a9747b200311", 
    inviteNow: false, 
    invitees: [
      {name: "Thomas Smith", email: "sthomas@youruniversity.edu"}
    ],
    participantType: Student
  ) {
    newParticipants {
      id
      participantType
      participantStatus
      user {
        name
      }
      invitationLink
      embedLoginLink
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```json
{
  "data": {
    "addCourseParticipants": {
      "newParticipants": [
        {
          "id": "5a7fdd2c-47d4-4a7f-8576-a640938e5574",
          "participantType": "Student",
          "participantStatus": "INVITATION_PENDING",
          "user": {
            "name": "Thomas Smith"
          },
          "invitationLink": "https://app.eduflow.com/join-from-email?userId=5a7fdd2c-47d4-4a7f-8576-a640938e5574&courseId=00000000-5945-95c7-65fc-a9747b200311&token=ezJ1c2VyX2lkIjoiNWE3ZmRkMmMtNDdkNC00YTdmLTg1NzYtYTY0MDkzOGU1YTc0In0.Mr15Hg.6CbxV2GGVNeKWc4C0xTn6CYTtm0",
          "embedLoginLink": "https://app.eduflow.com/join-from-email?userId=5a7fdd2c-47d4-4a7f-8576-a640938e5574&courseId=00000000-5945-95c7-65fc-a9747b200311&token=ezJ1c2VyX2lkIjoiNWE3ZmRkMmMtNDdkNC00YTdmLTg1NzYtYTY0MDkzOGU1YTc0In0.Mr15Hg.6CbxV2GGVNeKWc4C0xTn6CYTtm0&skip_signup=1"
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}

* `invitationLink`:  Will prompt user to create a password
* `embedLoginLink`:  "Magic link", takes user directly to to course

#### Removing participants

You can remove participants from course by email via `removeCourseParticipants`

{% hint style="info" %}
Course owner and institution owner cannot be removed from course and will be ignored.
{% endhint %}

{% tabs %}
{% tab title="Query" %}

```graphql
mutation {
  removeCourseParticipants(
    courseId: "00000000-5945-95c7-65fc-a9747b200311", 
    participantEmails: ["sthomas@youruniversity.edu"]
  ) {
    removedUsers {
      name
      email
    }
    course {
      title
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```json
{
  "data": {
    "removeCourseParticipants": {
      "removedUsers": [{
        "name": "Thomas Smith",
        "email": "sthomas@youruniversity.edu",
      }],
      "course": {
        "title": "Onboarding (New Employees)"
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}


# Flows and Activities

Flows and activities form the structure and content of your [courses](/guides/courses).

They're visible through the sidebar and Eduflow's URL structure.

`https://app.eduflow.com/courses/<course_id>/flows/<flow_id>/activities/<activity_id>`

Tip: Viewing the URL is a quick way to get the ID of a course, flow, activity, submission, or review.

## Flows

Flows are containers for activities.

Flows contain activities. Activities always have exactly one flow.

{% hint style="info" %}
Gotcha: Activities at the root-level of the sidebar belong to an invisible flow, "class-level activities".
{% endhint %}

## Activities

There are a growing list of activities for Eduflow. Activities accessible to your institution's course can be seen through the "Create Activity" button on the bottom right of the sidebar.

### Activity types

| Activity          | Description                                                                                                                             |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| Content           | Displays a read-only content to user                                                                                                    |
| Video             | Similar to Content, but allows for a Video upload or embedding Vimeo / YouTube                                                          |
| Submission        | Student submits content (Rich text, video recording/screencast, file upload, google drive upload)                                       |
| Self review       | Review of one's own Submission                                                                                                          |
| Instructor review | Instructor's review of Submission                                                                                                       |
| Peer review       | Student's are assigned to review each other's submissions                                                                               |
| Score             | Calculate custom, composite scores based on activity completion, review scores                                                          |
| HapYak            | Integration video courses from HapYak                                                                                                   |
| Zoom              | Integrate zoom calls                                                                                                                    |
| Grade passback    | [LTI Basic Outcomes v1.1](https://www.imsglobal.org/spec/lti-bo/v1p1/) support (single output supported, e.g. completion, review score) |
| Select a tag      | Lets students assign themselves to a tag in a course                                                                                    |
| Discussion        | Lets participants create topics and submit comments and replies                                                                         |

### Querying

Since there are many activity types, activities make use of [GraphQL Interfaces](https://graphql.org/learn/schema/#interfaces).

Common fields, such as `id` and `title` can be represented through `ActivityInterface`, via an [inline fragment](https://graphql.org/learn/queries/#inline-fragments). Interfaces and inline fragments are what allow structured, typed access to generic and specialized information in GraphQL.

{% content-ref url="/pages/-MT0xXxWSinLDmt7yaUA" %}
[Inline fragments](/graphql/inline-fragments)
{% endcontent-ref %}

#### Listing (inside course)

{% tabs %}
{% tab title="Query" %}

```graphql
{
  course(id: "00000000-5945-95c7-65fc-a9747b300320") {
    flows {
      title
      activities {
        ... on ActivityInterface {
          id
          title
          __typename
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "course": {
      "flows": [
        {
          "title": "Root flow",
          "activities": [
            {
              "id": "901abf26-f691-41d6-8777-a398e1f61b31",
              "title": "Welcome aboard!",
              "__typename": "ContentActivity"
            }
          ]
        },
        {
          "title": "Corporate Culture",
          "activities": [
            {
              "id": "faa38ffe-6d2c-4f4b-ae1d-cd43c616a7d2",
              "title": "Our Mission",
              "__typename": "ContentActivity"
            },
            {
              "id": "c5426418-0772-46b1-a84c-3f6f1e9d2925",
              "title": "Make an impact",
              "__typename": "SubmissionActivity"
            },
            {
              "id": "ae8f3183-0fd0-48e1-b52d-2f455cd78db7",
              "title": "Spot-check and team build",
              "__typename": "PeerReviewActivity"
            },
            {
              "id": "b59a7ae2-eeac-45ae-99b2-898f201c0955",
              "title": "Introspect and reflect",
              "__typename": "FeedbackReflectionActivity"
            },
            {
              "id": "b303c4ee-c450-4960-a951-082afe5b1c10",
              "title": "From your manager",
              "__typename": "TeacherReviewActivity"
            },
            {
              "id": "88ee9906-dc7d-4fa0-a13c-81ed68372ca6",
              "title": "How would you improve?",
              "__typename": "SelfReviewActivity"
            },
            {
              "id": "948561cc-5aed-4ff4-92a6-a761fc38d154",
              "title": "Water cooler",
              "__typename": "ContentActivity"
            },
            {
              "id": "73568026-fdd9-4c11-8662-bf5f291b99e3",
              "title": "Networking: Meet across divisions",
              "__typename": "ContentActivity"
            }
          ]
        },
        {
          "title": "Paper in the 2000's",
          "activities": [
            {
              "id": "f4017cb7-f924-4671-a9f1-913cc8244a3a",
              "title": "Paper: Relevant today?",
              "__typename": "ContentActivity"
            },
            {
              "id": "f79b1238-5b93-41bf-9af1-78a84b8fbcbb",
              "title": "How do you use paper?",
              "__typename": "SubmissionActivity"
            },
            {
              "id": "923944e7-51f8-4606-b2fa-f6d35fb0c158",
              "title": "Our philosophy",
              "__typename": "ContentActivity"
            }
          ]
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}

#### via ID

Specialty fields, such as `countStudentsCompleted` on `SubmissionActivity` can be reached by adding an [inline fragment](https://graphql.org/learn/queries/#inline-fragments).

{% tabs %}
{% tab title="Query" %}

```graphql
{
  activity(id: "c5426418-0772-46b1-a84c-3f6f1e9d2925") {
    ... on ActivityInterface {
      id
      title
      __typename
    }
    ... on SubmissionActivity {
      countStudentsCompleted
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "activity": {
      "id": "c5426418-0772-46b1-a84c-3f6f1e9d2925",
      "title": "Make an impact",
      "__typename": "SubmissionActivity",
      "countStudentsCompleted": 8
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Mutations

#### Mark activities as complete

`markActivityComplete`

{% tabs %}
{% tab title="Query" %}

```graphql
mutation {
  markActivityComplete(
    activityId: "5b1f1253-68ea-4583-bfff-0ae5938fcd64", 
    studentEmail: "student@example.edu"
  ) {
    activity {
      ... on ActivityInterface {
        title
      }
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "markActivityComplete": {
      "activity": {
        "title": "Onboarding (New Employees)"
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

## Scores and calculations

To learn more about querying the scores of Reviews, see the Outputs summary section:

{% content-ref url="/pages/-MS-rj\_H0YNbUm1nrFcS" %}
[Course summary (outputs)](/guides/course-summary-outputs)
{% endcontent-ref %}


# Reviews and Reflections

## Review activities

Variants: `PeerReview`, `InstructorReview`, `SelfReview`

### Rubrics: What makes a review?

A review activity consists of a rubric. Rubrics are a list of questions, some of which are scorable (Scale, Boolean questions) and others which are purely qualitative (Text questions)

An instructor will customize a rubric before students start reviewing (as editing will be locked after students begin reviewing)

{% hint style="info" %}
If you want to have a score calculation, ensure that you have **Scale** and **Boolean** questions set, if there's only text questions in your rubric, scores will be empty.
{% endhint %}

### API Example

Reviews are available via a pageable [GraphQL Connection](https://graphql.org/learn/pagination/#end-of-list-counts-and-connections):

{% content-ref url="/pages/-MT0jmlOIXnJAuofcp4E" %}
[Paginating queries](/graphql/pagination)
{% endcontent-ref %}

{% tabs %}
{% tab title="Query" %}

```graphql
{
  activity(id: "ae8f3183-0fd0-48e1-b52d-2f455cd78db7") {
    ... on PeerReviewActivity {
      title
      reviews(first: 2) {
        edges {
          node {
            id
            reviewType
            createdAt
            receivers {
              name
              email
            }
            givers {
              name
              email
            }
            answers {
              textAnswer
              scaleAnswer
              scaleAnswerPercentage
              question {
                questionType
                htmlText
                negativeAnswerText
                positiveAnswerText
                isOptional
                minNumWords
              }
            }
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "activity": {
      "title": "Spot-check and team build",
      "reviews": {
        "edges": [
          {
            "node": {
              "id": "44888cf6-31da-49e2-a707-318e0d36a4bf",
              "reviewType": "PeerReview",
              "createdAt": "2021-02-08T17:40:06.629884",
              "receivers": [
                {
                  "name": "Ryan Howard",
                  "email": "ryan.howard@dunder-mifflin.com"
                }
              ],
              "givers": [
                {
                  "name": "Pam Beesley",
                  "email": "pam@dunder-mifflin.com"
                }
              ],
              "answers": [
                {
                  "textAnswer": "Some awesome answer for question \"Question of type Text\" (530ac85d-4729-490b-8ac5-9ca263bbebd1) Skill debate couple society fear war myself decade reality whether east similar here action system I those form ahead kind attorney former fund today who politics to too several lay region fund center perhaps month green college him exactly drive support eat order moment white single nature more responsibility throughout stop whether after hope suddenly they indeed free out employee because others positive song trip pick remember executive a south story spring magazine unit my enjoy where responsibility forget agreement support head represent whom country old instead environmental plan professional choose can prepare television allow billion bar senior mean.",
                  "scaleAnswer": null,
                  "scaleAnswerPercentage": null,
                  "question": {
                    "questionType": "Text",
                    "htmlText": "Question of type Text",
                    "negativeAnswerText": null,
                    "positiveAnswerText": null,
                    "isOptional": true,
                    "minNumWords": 6
                  }
                },
                {
                  "textAnswer": null,
                  "scaleAnswer": 4,
                  "scaleAnswerPercentage": 100,
                  "question": {
                    "questionType": "Scale",
                    "htmlText": "Question of type Scale",
                    "negativeAnswerText": null,
                    "positiveAnswerText": null,
                    "isOptional": false,
                    "minNumWords": null
                  }
                },
                {
                  "textAnswer": null,
                  "scaleAnswer": null,
                  "scaleAnswerPercentage": null,
                  "question": {
                    "questionType": "Boolean",
                    "htmlText": "Question of type Boolean",
                    "negativeAnswerText": "Nay",
                    "positiveAnswerText": "Yay",
                    "isOptional": false,
                    "minNumWords": null
                  }
                }
              ]
            }
          },
          {
            "node": {
              "id": "bc151c68-45a3-466e-aeb9-8695713148b1",
              "reviewType": "PeerReview",
              "createdAt": "2021-02-08T17:40:06.670883",
              "receivers": [
                {
                  "name": "Karen Filipelli",
                  "email": "karen.filipelli@dunder-mifflin.com"
                }
              ],
              "givers": [
                {
                  "name": "Pam Beesley",
                  "email": "pam@dunder-mifflin.com"
                }
              ],
              "answers": [
                {
                  "textAnswer": "Some awesome answer for question \"Question of type Text\" (530ac85d-4729-490b-8ac5-9ca263bbebd1) In five sing yet alone issue shake stuff vote during far until into five behind full white suddenly big white produce full trouble short various and my save eight manage service medical lead eat nice audience manage they return building pattern green international follow a our pass amount stage pick traditional music different other view area child add south account teach participant necessary four perhaps training sea remain feeling minute assume success make myself where couple reality ball part growth politics suddenly cause people ago together draw audience future evening stock general how career provide improve word simple two whose wide challenge test though shoulder policy gas food firm security majority person traditional half rate soldier her seek hit Mrs.",
                  "scaleAnswer": null,
                  "scaleAnswerPercentage": null,
                  "question": {
                    "questionType": "Text",
                    "htmlText": "Question of type Text",
                    "negativeAnswerText": null,
                    "positiveAnswerText": null,
                    "isOptional": true,
                    "minNumWords": 6
                  }
                },
                {
                  "textAnswer": null,
                  "scaleAnswer": 3,
                  "scaleAnswerPercentage": 75,
                  "question": {
                    "questionType": "Scale",
                    "htmlText": "Question of type Scale",
                    "negativeAnswerText": null,
                    "positiveAnswerText": null,
                    "isOptional": false,
                    "minNumWords": null
                  }
                },
                {
                  "textAnswer": null,
                  "scaleAnswer": null,
                  "scaleAnswerPercentage": null,
                  "question": {
                    "questionType": "Boolean",
                    "htmlText": "Question of type Boolean",
                    "negativeAnswerText": "Nay",
                    "positiveAnswerText": "Yay",
                    "isOptional": false,
                    "minNumWords": null
                  }
                }
              ]
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

## Feedback Reflections

{% hint style="info" %}
Peergrade user? These are the Eduflow counterpart for **Reactions**
{% endhint %}

These are responses to reviews. They have a similar internal structure to reviews, since they're both answers submitted against questions in a rubric.

### API Example

The same as Review activities, except using `reflections` (`FeedbackReflectionConnection`) instead of `reviews`:

{% tabs %}
{% tab title="Query" %}

```graphql
{
  activity(id: "b59a7ae2-eeac-45ae-99b2-898f201c0955") {
    ... on FeedbackReflectionActivity {
      title
      reflections(first: 2) {
        edges {
          node {
            id
            reviewType
            createdAt
            receivers {
              name
              email
            }
            givers {
              name
              email
            }
            answers {
              textAnswer
              scaleAnswer
              scaleAnswerPercentage
              question {
                questionType
                htmlText
                negativeAnswerText
                positiveAnswerText
                isOptional
                minNumWords
              }
            }
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "activity": {
      "title": "Introspect and reflect",
      "reflections": {
        "edges": [
          {
            "node": {
              "id": "d521373e-c36b-40f3-bdbd-e6f2b85e5927",
              "reviewType": "FeedbackReflection",
              "createdAt": "2021-02-08T17:40:08.877703",
              "receivers": [
                {
                  "name": "Pam Beesley",
                  "email": "pam@dunder-mifflin.com"
                }
              ],
              "givers": [
                {
                  "name": "Phyllis Vance",
                  "email": "phyllis.vance@dunder-mifflin.com"
                },
                {
                  "name": "Andy Bernard",
                  "email": "andy.bernard@dunder-mifflin.com"
                },
                {
                  "name": "Meredith Palmer",
                  "email": "meredith.palmer@dunder-mifflin.com"
                },
                {
                  "name": "Karen Filipelli",
                  "email": "karen.filipelli@dunder-mifflin.com"
                },
                {
                  "name": "Creed Bratton",
                  "email": "creed.bratton@dunder-mifflin.com"
                },
                {
                  "name": "Jim Halpert",
                  "email": "jim@dunder-mifflin.com"
                },
                {
                  "name": "Ryan Howard",
                  "email": "ryan.howard@dunder-mifflin.com"
                },
                {
                  "name": "Erin Hannon",
                  "email": "erin.hannon@dunder-mifflin.com"
                },
                {
                  "name": "Oscar Martinez",
                  "email": "oscar.martinez@dunder-mifflin.com"
                },
                {
                  "name": "Kelly Kapoor",
                  "email": "kelly.kapoor@dunder-mifflin.com"
                },
                {
                  "name": "Kevin Mallone",
                  "email": "kevin.mallone@dunder-mifflin.com"
                },
                {
                  "name": "Stanley Hudson",
                  "email": "stanley@dunder-mifflin.com"
                },
                {
                  "name": "Toby Flenderson",
                  "email": "toby.flenderson@dunder-mifflin.com"
                },
                {
                  "name": "Angela Martin",
                  "email": "angela.martin@dunder-mifflin.com"
                },
                {
                  "name": "Pam Beesley",
                  "email": "pam@dunder-mifflin.com"
                }
              ],
              "answers": []
            }
          },
          {
            "node": {
              "id": "4c3d4d7c-205c-4f55-8fc1-c138576c2929",
              "reviewType": "FeedbackReflection",
              "createdAt": "2021-02-08T17:40:08.894052",
              "receivers": [
                {
                  "name": "Pam Beesley",
                  "email": "pam@dunder-mifflin.com"
                }
              ],
              "givers": [
                {
                  "name": "Karen Filipelli",
                  "email": "karen.filipelli@dunder-mifflin.com"
                }
              ],
              "answers": [
                {
                  "textAnswer": "Some awesome answer for question \"What did you learn from the feedback you received?\" (2f7c267b-209a-49a3-8368-1a4dffe03050) Song none now author know sit growth experience left care leave much parent grow might various assume debate listen heavy table perhaps table probably drug weight tree strategy study respond rule throw set new least space last personal fine since at because evidence increase song memory buy will out mother more treat or machine section manage air by police identify Mr remember something compare order himself wrong political on animal would event pretty teach yourself put check his admit space start company conference let fill lay center chance space information carry Congress usually never politics his edge old author person sing bill political civil professor who yourself option among chair culture factor recent out about summer involve ago anything course every just nothing spend notice stop tax speak reason mention her team catch radio think administration.",
                  "scaleAnswer": null,
                  "scaleAnswerPercentage": null,
                  "question": {
                    "questionType": "Text",
                    "htmlText": "What did you learn from the feedback you received?",
                    "negativeAnswerText": null,
                    "positiveAnswerText": null,
                    "isOptional": false,
                    "minNumWords": 1
                  }
                },
                {
                  "textAnswer": "Some awesome answer for question \"How do you plan on using that feedback to improve?\" (4a02627a-c77a-4801-87ab-0fc8a9b00330) Forward happen season floor market kid follow prove describe late method yes return policy particular off need out last happen great foreign student section recently real wide key seven house her hand fine let tree myself worry then garden Congress maintain first quite data building style leader at nearly well continue free upon from dark sport authority follow drive step serious keep discussion huge something back start east first perform guy moment garden student her ready sure feel little deal develop industry run cell prepare much run thought watch four personal face prove two your class later.",
                  "scaleAnswer": null,
                  "scaleAnswerPercentage": null,
                  "question": {
                    "questionType": "Text",
                    "htmlText": "How do you plan on using that feedback to improve?",
                    "negativeAnswerText": null,
                    "positiveAnswerText": null,
                    "isOptional": false,
                    "minNumWords": 1
                  }
                },
                {
                  "textAnswer": null,
                  "scaleAnswer": 2,
                  "scaleAnswerPercentage": 50,
                  "question": {
                    "questionType": "Scale",
                    "htmlText": "How useful is the feedback?",
                    "negativeAnswerText": null,
                    "positiveAnswerText": null,
                    "isOptional": false,
                    "minNumWords": null
                  }
                }
              ]
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}


# Discussions

### Topics & Comments

A `DiscussionActivity` consists of topics. A `Topic` contains a connection to `Comment`, which contains the top-level comments, then every `Comment` contains a connection to `Comment` for replies.

{% hint style="info" %}
Comment replies in Eduflow go one level deep only.
{% endhint %}

### API Example

Topics and Comments are available via a pageable [GraphQL Connection](https://graphql.org/learn/pagination/#end-of-list-counts-and-connections):

{% content-ref url="/pages/-MT0jmlOIXnJAuofcp4E" %}
[Paginating queries](/graphql/pagination)
{% endcontent-ref %}

{% tabs %}
{% tab title="Query" %}

```graphql
{
  activity(id: "ae8f3183-0fd0-48e1-b52d-2f455cd78db7") {
    ... on DiscussionActivity {
      title
      topics(first: 2) {
        edges {
          node {
            title
            content
            likesCount
            author {
              name
            }
            commentsCount
            comments {
              edges {
                node {
                  content
                  likesCount
                  repliesCount
                  author {
                    name
                  }
                  replies {
                    edges {
                      node {
                        content
                        author {
                          name
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}

```javascript
{
    'data': {
        'activity': {
            'topics': {
                'totalCount': 2,
                'edges': [
                    {
                        'node': {
                            'title': 'Topic 2',
                            'content': '<p>Topic 2</p>',
                            'author': {
                                'name': 'Ryan Howard'
                            },
                            'commentsCount': 0,
                            'comments': {
                                'edges': [
                                ]
                            },
                            'likesCount': 0
                        }
                    },
                    {
                        'node': {
                            'title': 'Topic 1',
                            'content': '<p>Topic 1</p>',
                            'author': {
                                'name': 'Ryan Howard'
                            },
                            'likesCount': 2,
                            'commentsCount': 4,
                            'comments': {
                                'edges': [
                                    {
                                        'node': {
                                            'author': {
                                                'name': 'Ryan Howard'
                                            },
                                            'content': '<p>Comment 1</p>',
                                            'likesCount': 2,
                                            'replies': {
                                                'edges': [
                                                    {
                                                        'node': {
                                                            'author': {
                                                                'name': 'Ryan Howard'
                                                            },
                                                            'content': '<p>Reply 1</p>'
                                                        }
                                                    },
                                                    {
                                                        'node': {
                                                            'author': {
                                                                'name': 'Ryan Howard'
                                                            },
                                                            'content': '<p>Reply 2</p>'
                                                        }
                                                    }
                                                ]
                                            },
                                            'repliesCount': 2
                                        }
                                    },
                                    {
                                        'node': {
                                            'author': {
                                                'name': 'Pam Beesley'
                                            },
                                            'content': '<p>Comment 2</p>',
                                            'likesCount': 0,
                                            'replies': {
                                                'edges': [
                                                ]
                                            },
                                            'repliesCount': 0
                                        }
                                    }
                                ]
                            }
                        }
                    }
                ]
            }
        }
    }
}
```

{% endtab %}
{% endtabs %}


# Quizzes

### Quiz

A `QuizActivity` has associated a `Quiz` object that contains the set of the quiz questions and the learners responses to the quiz.

### Questions

The quiz questions can be either of type `MultipleChoice`, where learners can select one or more option, or `OpenEnded`, where learners can write an open ended answer.

#### API Example

Since there are different question types, quiz questions make use of [GraphQL Interfaces](https://graphql.org/learn/schema/#interfaces). Common fields, such as `id` and `content` can be represented through `QuizQuestion` interface, specific fields can be fetched via an [inline fragment](https://graphql.org/learn/queries/#inline-fragments).

{% content-ref url="/pages/-MT0xXxWSinLDmt7yaUA" %}
[Inline fragments](/graphql/inline-fragments)
{% endcontent-ref %}

{% tabs %}
{% tab title="Query" %}

```
query {
  activity(id: "db79e3a7-07e7-4b2f-b799-e00ed94c1c8a") {
    __typename
    ... on QuizActivity {
      quiz {
        questions {
          __typename
          content
          order
          isOptional
          questionType
          ... on QuizQuestionMultipleChoice {
            selectionMode
            randomizeAnswers
            options {
              order
              content
            }
          }
        }
      }
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```
{
  "data": {
    "activity": {
      "__typename": "QuizActivity",
      "quiz": {
        "questions": [
          {
            "__typename": "QuizQuestionMultipleChoice",
            "content": "<p>Question 1</p>",
            "order": 0,
            "isOptional": false,
            "questionType": "MultipleChoice",
            "selectionMode": "Single",
            "randomizeAnswers": false,
            "options": [
              {
                "order": 0,
                "content": "<p>Option 1</p>"
              },
              {
                "order": 1,
                "content": "<p>Option 2</p>"
              },
              {
                "order": 2,
                "content": "<p>Option 3</p>"
              }
            ]
          },
          {
            "__typename": "QuizQuestionMultipleChoice",
            "content": "<p>Question 2</p>",
            "order": 1,
            "isOptional": false,
            "questionType": "MultipleChoice",
            "selectionMode": "Multiple",
            "randomizeAnswers": false,
            "options": [
              {
                "order": 0,
                "content": "<p>Option 1</p>"
              },
              {
                "order": 1,
                "content": "<p>Option 2</p>"
              },
              {
                "order": 2,
                "content": "<p>Option 3</p>"
              }
            ]
          },
          {
            "__typename": "QuizQuestionMultipleChoice",
            "content": "<p>Question 3</p>",
            "order": 2,
            "isOptional": true,
            "questionType": "MultipleChoice",
            "selectionMode": "Multiple",
            "randomizeAnswers": true,
            "options": [
              {
                "order": 0,
                "content": "<p>Option 1</p>"
              },
              {
                "order": 1,
                "content": "<p>Option 2</p>"
              },
              {
                "order": 2,
                "content": "<p>Option 3</p>"
              }
            ]
          },
          {
            "__typename": "QuizQuestionOpenEnded",
            "content": "<p>Question 4</p>",
            "order": 3,
            "isOptional": false,
            "questionType": "OpenEnded"
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Responses

The quiz responses contains the learners answers to the quiz. Each answer can be either `MultipleChoice` or `OpenEnded`, depending on the question type.

#### API Example

Responses are available via a pageable [GraphQL Connection](https://graphql.org/learn/pagination/#end-of-list-counts-and-connections).

{% content-ref url="/pages/-MT0jmlOIXnJAuofcp4E" %}
[Paginating queries](/graphql/pagination)
{% endcontent-ref %}

{% tabs %}
{% tab title="Query" %}

```graphql
query {
  activity(id: "db79e3a7-07e7-4b2f-b799-e00ed94c1c8a") {
    __typename
    ... on QuizActivity {
      quiz {
        responses(first: 2) {
          edges {
            node {
              retryNumber
              submitter {
                name
              }
              group {
                members {
                  name
                }
              }
              answers {
                __typename
                question {
                  content
                  questionType
                }
                ... on QuizAnswerMultipleChoice {
                  selectedOptions {
                    content
                    order
                  }
                  isCorrect
                }
                ... on QuizAnswerOpenEnded {
                  content  
                }
              }
            }
          }
        }
      }
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "activity": {
      "__typename": "QuizActivity",
      "quiz": {
        "responses": {
          "edges": [
            {
              "node": {
                "retryNumber": null,
                "submitter": {
                  "name": "Daisy Duck"
                },
                "group": {
                  "members": [
                    {
                      "name": "Daisy Duck"
                    }
                  ]
                },
                "answers": [
                  {
                    "__typename": "QuizAnswerMultipleChoice",
                    "question": {
                      "content": "<p>Question 1</p>",
                      "questionType": "MultipleChoice"
                    },
                    "selectedOptions": [
                      {
                        "content": "<p>Option 1</p>",
                        "order": 0
                      }
                    ],
                    "isCorrect": true
                  },
                  {
                    "__typename": "QuizAnswerMultipleChoice",
                    "question": {
                      "content": "<p>Question 2</p>",
                      "questionType": "MultipleChoice"
                    },
                    "selectedOptions": [
                      {
                        "content": "<p>Option 2</p>",
                        "order": 1
                      }
                    ],
                    "isCorrect": false
                  },
                  {
                    "__typename": "QuizAnswerMultipleChoice",
                    "question": {
                      "content": "<p>Question 3</p>",
                      "questionType": "MultipleChoice"
                    },
                    "selectedOptions": [
                      {
                        "content": "<p>Option 2</p>",
                        "order": 1
                      },
                      {
                        "content": "<p>Option 3</p>",
                        "order": 2
                      }
                    ],
                    "isCorrect": false
                  },
                  {
                    "__typename": "QuizAnswerOpenEnded",
                    "question": {
                      "content": "<p>Question 4</p>",
                      "questionType": "OpenEnded"
                    },
                    "content": "<p>Open answer</p>"
                  }
                ]
              }
            },
            {
              "node": {
                "retryNumber": null,
                "submitter": {
                  "name": "Uncle Scrooge"
                },
                "group": {
                  "members": [
                    {
                      "name": "Uncle Scrooge"
                    }
                  ]
                },
                "answers": [
                  {
                    "__typename": "QuizAnswerMultipleChoice",
                    "question": {
                      "content": "<p>Question 1</p>",
                      "questionType": "MultipleChoice"
                    },
                    "selectedOptions": [
                      {
                        "content": "<p>Option 2</p>",
                        "order": 1
                      }
                    ],
                    "isCorrect": false
                  },
                  {
                    "__typename": "QuizAnswerMultipleChoice",
                    "question": {
                      "content": "<p>Question 2</p>",
                      "questionType": "MultipleChoice"
                    },
                    "selectedOptions": [
                      {
                        "content": "<p>Option 2</p>",
                        "order": 1
                      },
                      {
                        "content": "<p>Option 3</p>",
                        "order": 2
                      }
                    ],
                    "isCorrect": false
                  },
                  {
                    "__typename": "QuizAnswerMultipleChoice",
                    "question": {
                      "content": "<p>Question 3</p>",
                      "questionType": "MultipleChoice"
                    },
                    "selectedOptions": [
                      {
                        "content": "<p>Option 1</p>",
                        "order": 0
                      },
                      {
                        "content": "<p>Option 3</p>",
                        "order": 2
                      }
                    ],
                    "isCorrect": false
                  },
                  {
                    "__typename": "QuizAnswerOpenEnded",
                    "question": {
                      "content": "<p>Question 4</p>",
                      "questionType": "OpenEnded"
                    },
                    "content": "<p>Open answer</p>"
                  }
                ]
              }
            }
          ]
        }
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}


# Users & Participants

## Institution and course owner

Users exist in a variety of levels in Eduflow. [Institutions](/guides/institutions) and [courses](/guides/courses) allow direct access to their owner. In GraphQL, these are associated with the `User` object type.

### Institution owner

{% tabs %}
{% tab title="Query" %}

```graphql
{
  institution {
    name
    owner {
      name
      email
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "institution": {
      "name": "Dunder Mifflin",
      "owner": {
        "name": "Michael Scott",
        "email": "michael.scott@dunder-mifflin.com"
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Course owner

{% tabs %}
{% tab title="Query" %}

```graphql
{
  course(id: "00000000-5945-95c7-65fd-a9747b200320") {
    owner {
      name
      email
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "course": {
      "owner": {
        "name": "John Smith",
        "email": "john.smith@example.edu"
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

## Course participants

In [courses](/guides/courses), users are queried through a pageable [connection](/graphql/pagination): `Course.participants`. Since course users have course-specific context associated them (their participant type and status), their `User` objects are wrapped in `Participant`.&#x20;

Participant types: `Owner` (creator of course),  `Admin`s, `Instructor`s, `Assistant`s and `Student`s.

{% tabs %}
{% tab title="Query" %}

```graphql
{
  course(id: "00000000-5944-95c7-65fc-a9747b200320") {
    participants(first: 5) {
      edges {
        node {
          participantType
          participantStatus
          user {
            name
            email
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "course": {
      "participants": {
        "edges": [
          {
            "node": {
              "participantType": "Owner",
              "participantStatus": "ACTIVE",
              "user": {
                "name": "Michael Scarn",
                "email": "michael.scott@dunder-mifflin"
              }
            }
          },
          {
            "node": {
              "participantType": "Teacher",
              "participantStatus": "ACTIVE",
              "user": {
                "name": "John Doe",
                "email": "john.doe@example.edu"
              }
            }
          },
          {
            "node": {
              "participantType": "Teacher",
              "participantStatus": "ACTIVE",
              "user": {
                "name": "Gyro",
                "email": "abba.g@example.eu"
              }
            }
          },
          {
            "node": {
              "participantType": "Assistant",
              "participantStatus": "ACTIVE",
              "user": {
                "name": "Dwight Schrute",
                "email": "schrute@dunder-mifflin.com"
              }
            }
          },
          {
            "node": {
              "participantType": "Student",
              "participantStatus": "ACTIVE",
              "user": {
                "name": "Jim Halpert",
                "email": "jim@dundner-mifflin.com"
              }
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Searching by username

{% tabs %}
{% tab title="Query" %}

```graphql
{
  course(id: "00000000-5945-95c7-65fc-a9747b2003210") {
    participants(first: 1, searchTerm: "Angela") {
      edges {
        node {
          participantType
          participantStatus
          user {
            name
            email
          }
        }
      }
    }
  }
}
```

{% endtab %}

{% tab title="Result" %}

```javascript
{
  "data": {
    "course": {
      "participants": {
        "edges": [
          {
            "node": {
              "participantType": "Student",
              "participantStatus": "ACTIVE",
              "user": {
                "name": "Angela Martin",
                "email": "angela.martin@dunder-mifflin.com"
              }
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Searching by participant type:

{% tabs %}
{% tab title="Query" %}

```graphql
{
  course(id: "00000000-5945-95c7-65fc-a9747b200120") {
    participants(first: 5, participantTypeFilter: Student) {
      edges {
        node {
          participantType
          participantStatus
          user {
            name
            email
          }
        }
      }
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "course": {
      "participants": {
        "edges": [
          {
            "node": {
              "participantType": "Student",
              "participantStatus": "ACTIVE",
              "user": {
                "name": "Pam Beesly",
                "email": "pam.beesly@dunder-mifflin.com"
              }
            }
          },
          {
            "node": {
              "participantType": "Student",
              "participantStatus": "ACTIVE",
              "user": {
                "name": "Ryan Howard",
                "email": "ryan.howard@dunder-mifflin.com"
              }
            }
          },
          {
            "node": {
              "participantType": "Student",
              "participantStatus": "ACTIVE",
              "user": {
                "name": "Kevin Malone",
                "email": "kevin.malone@dunder-mifflin.com"
              }
            }
          },
          {
            "node": {
              "participantType": "Student",
              "participantStatus": "ACTIVE",
              "user": {
                "name": "Andy Bernard",
                "email": "andy.bernard@dunder-mifflin.com"
              }
            }
          },
          {
            "node": {
              "participantType": "Student",
              "participantStatus": "ACTIVE",
              "user": {
                "name": "Phyllis Smith",
                "email": "phyllis.smith@dunder-mifflin.com"
              }
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Individual participant lookup

{% tabs %}
{% tab title="Query" %}

```graphql
{
  course(id: "00000000-5943-95c7-65fc-a9747b200320") {
    participants(id: "7a41ad8e-f252-449d-8add-13c8ae27470b") {
      edges {
        node {
          participantType
          participantStatus
          user {
            name
            email
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "course": {
      "participants": {
        "edges": [
          {
            "node": {
              "participantType": "Student",
              "participantStatus": "ACTIVE",
              "user": {
                "name": "Pam Beesly",
                "email": "pam.beesly@dunder-mifflin.com"
              }
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Participant invite links

Invitation links and embed (a.k.a magic links) can be accessed through the `Participant` response object:

{% tabs %}
{% tab title="Query" %}

```graphql
{
  course(id: "00000000-5943-95c7-65fc-a9747b200320") {
    owner {
      name
      email
    }
    participants {
      edges {
        node {
          participantStatus
          invitationLink
          embedLoginLink
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}

```json
{
  "data": {
    "course": {
      "participants": [
        {
          "id": "5a7fdd2c-47d4-4a7f-8576-a640938e5574",
          "participantType": "Student",
          "participantStatus": "INVITATION_PENDING",
          "user": {
            "name": "Thomas Smith"
          },
          "invitationLink": "https://app.eduflow.com/join-from-email?userId=5a7fdd2c-47d4-4a7f-8576-a640938e5574&courseId=00000000-5945-95c7-65fc-a9747b200320&token=eyJ1c2VyX2lkIjoiNWE3ZmRkMmMtNDdkNC00YTdmLTg1NzYtYTY0MDkzOGU1NTc0In0.Fr75Hg.6VbxV2GGVNeKWc4C0xTn5CYTtm0",
          "embedLoginLink": "https://app.eduflow.com/join-from-email?userId=5a7fdd2c-47d4-4a7f-8576-a640938e5574&courseId=00000000-5945-95c7-65fc-a9747b200320&token=eyJ1c2VyX2lkIjoiNWE3ZmRkMmMtNDdkNC00YTdmLTg1NzYtYTY0MDkzOGU1NTc0In0.Fr75Hg.6VbxV2GGVNeKWc4C0xTn5CYTtm0&skip_signup=1"
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}

* `invitationLink`:  Will prompt user to create a password
* `embedLoginLink`:  "Magic link", takes user directly to to course


# Tags

Instructors can create course tags and assign them to students. Tags are used in features like activity subsetting or groups.&#x20;

{% hint style="info" %}
A tag belongs to a single course but a course can have multiple tags. \
Tags and students are a many-to-many relationship.
{% endhint %}

## Querying

### Course tags

Course tags can be listed by using the `Course.tags` field. This returns a list of `Tag` models that contain information like `label`, `color` or the assigned `students`. For more details, check out the [`Tag` model in the API Reference](https://graphdoc.eduflow.com/tag.doc.html).&#x20;

The `Tag.students` field is a GraphQL connection. Read how to paginate a connection in the [dedicated guide](/graphql/pagination).&#x20;

{% tabs %}
{% tab title="Query" %}

```graphql
{
  course(id: "00000000-5945-95c7-65fc-a9747b200320") {
    tags {
      label
      color
      students {
        edges {
          node {
            name
            email
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "course": {
      "tags": [
        {
          "label": "Stark",
          "color": "#EB5757FF",
          "students": {
            "edges": [
              {
                "node": {
                  "name": "Sansa Stark",
                  "email": "sansa@example.com"
                }
              },
              {
                "node": {
                  "name": "Ned Stark",
                  "email": "ned@starks.com"
                }
              }
            ]
          }
        },
        {
          "label": "Lannister",
          "color": "#2F80EDFF",
          "students": {
            "edges": [
              {
                "node": {
                  "name": "Jaime Lannister",
                  "email": "jaime@example.com"
                }
              }
            ]
          }
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Participant tags

Tags can also be queried from the `Participant` model. The `Participant.tags` field lists all the tags the student was assigned within the course.&#x20;

Tags can only be applied to students so other participant types will have a `null` value when querying for the `Participant.tags` field. For students, the API will always return a list.

{% tabs %}
{% tab title="Query" %}

```graphql
{
  course(id: "00000000-5945-95c7-65fc-a9747b200320") {
    participants {
      edges {
        node {
          user {
            name
          }
          participantType
          tags {
            label
            color
          }
        }
      }
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "course": {
      "participants": {
        "edges": [
          {
            "node": {
              "user": {
                "name": "Ned Stark"
              },
              "participantType": "Owner",
              "tags": null
            }
          },
          {
            "node": {
              "user": {
                "name": "Arya Stark"
              },
              "participantType": "Student",
              "tags": [
                {
                  "label": "House Stark",
                  "color": "#EB5757FF"
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "name": "Bran Stark"
              },
              "participantType": "Student",
              "tags": [
                {
                  "label": "House Stark",
                  "color": "#EB5757FF"
                },
                {
                  "label": "King of the Six Kingdoms",
                  "color": "#AA1212FF"
                }
              ]
            }          
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

## Mutations

### Create tag

You can create tags via the `createTag` mutation

{% hint style="info" %}
The tag color should be in `#RRGGBBAA` format
{% endhint %}

{% tabs %}
{% tab title="Mutation" %}

```graphql
mutation {
  createTag(
    courseId: "00000000-5945-95c7-65fc-a9747b200320",
    label: "Westeros",
    color: "#A32533AB"
  ) {
    tag {
      label
			color
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "createTag": {
      "tag": {
        "label": "Westeros",
        "color": "#112233FF"
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}

### Add students to tag

You can add students to a tag via the `addStudentsToTag` mutation. The mutation returns the updated tag so details like the complete list of `students` can be fetched. The `studentIds` are `Participant.id` values that can be fetched from the `Course.participants` connection.

{% hint style="info" %}
Based on the tag id, only student ids from tag's course are accepted
{% endhint %}

{% tabs %}
{% tab title="Mutation" %}

```graphql
mutation {
  addStudentsToTag(
    tagId: "de1bf57b-aa1d-4736-987e-dc0c4d3620d5", 
    studentIds: ["21b26c40-83ea-43a2-9c4b-f88722e0b9be"]
  ) {
    tag {
      label
      students {
        edges {
          node {
            name
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "addStudentsToTag": {
      "tag": {
        "label": "Westeros",
        "students": {
          "edges": [
            {
              "node": {
                "name": "Tyrion Lannister"
              }
            },
            {
              "node": {
                "name": "Ser Jorah"
              }
            },
            {
              "node": {
                "name": "Hodor"
              }
            }
          ]
        }
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}


# Course summary (outputs)

Scores and metadata

Eduflow provides access to various computed results via your course's activity page

![](https://2518674650-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GmK_EkZyQQJhTJyLh%2F-MS-qSC1fTkfcUAyo4_n%2F-MS-sAsV8Gq5DPfz2aCg%2Fimage.png?alt=media\&token=8e883dcb-5ed4-4948-b219-f48d1e326ff7)

From within, you can access:

| Table        | Values                                                                                              |
| ------------ | --------------------------------------------------------------------------------------------------- |
| **Progress** | Completion state / status of activities                                                             |
| **Scores**   | Submission, review quality (%)                                                                      |
| **Metadata** | Completion date, file size, word count, reviews/reflections completed, Topics/comments/likes posted |

![](https://2518674650-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M3GmK_EkZyQQJhTJyLh%2F-MS-qSC1fTkfcUAyo4_n%2F-MS-sNewGmwWalw9wxYm%2Fimage.png?alt=media\&token=d39fc6f9-ee1f-47a2-9ddb-9bbe58d49240)

### Querying

#### Score activity

This example retrieves score completion state and score outputs.

{% tabs %}
{% tab title="Query" %}

```graphql
query ScoreActivityQuery {
  activity(id: "<activity_id>") {
    ... on ScoringActivity {
      outputs {
        edges {
          node {
            user {
              username
              email
            }
            outputs {
              ... on CompletionState {
                outputType
                status
                isOverridden
              }
              ... on Score {
                outputType
                score
              }
            }
          }
        }
      }
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```json
{
  "data": {
    "activity": {
      "outputs": {
        "edges": [
          {
            "node": {
              "user": {
                "username": "oscar.martinez@dunder-mifflin.com",
                "email": "oscar.martinez@dunder-mifflin.com"
              },
              "outputs": [
                {},
                {
                  "outputType": "CompletionState",
                  "status": "Finished",
                  "isOverridden": false
                },
                {
                  "outputType": "Score",
                  "score": "1.0"
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": "angela.martin@dunder-mifflin.com",
                "email": "angela.martin@dunder-mifflin.com"
              },
              "outputs": [
                {},
                {
                  "outputType": "CompletionState",
                  "status": "Finished",
                  "isOverridden": false
                },
                {
                  "outputType": "Score",
                  "score": "0.3666666666666667"
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": null,
                "email": "creed.bratton@dunder-mifflin.com"
              },
              "outputs": [
                {},
                {
                  "outputType": "CompletionState",
                  "status": "Unstarted",
                  "isOverridden": false
                },
                {
                  "outputType": "Score",
                  "score": null
                }
              ]
            }
          }
        ]
      }
    }
  }
}espon
```

{% endtab %}
{% endtabs %}

#### Submission Activity

This examples has fragments for completion, file size, and (word) count.

{% tabs %}
{% tab title="Query" %}

```graphql
query SubmissionActivityQuery {
  activity(id: "<activity_id>") {
    ... on SubmissionActivity {
      outputs {
        edges {
          node {
            user {
              username
              email
            }
            outputs {
              ... on CompletionState {
                outputType
                status
                isOverridden
              }
              ... on CompletionDate {
                outputType
                completionDate
              }
              ... on FileSize {
                outputType
                sizeBytes
              }
              ... on Count {
                outputType
                max
                count
              }
            }
          }
        }
      }
    }
  }
}

```

{% endtab %}

{% tab title="Response" %}

```javascript
{
  "data": {
    "activity": {
      "outputs": {
        "edges": [
          {
            "node": {
              "user": {
                "username": "andy",
                "email": "andy.bernard@dunder-mifflin.com"
              },
              "outputs": [
                {
                  "outputType": "CompletionState",
                  "status": "Finished",
                  "isOverridden": false
                },
                {
                  "outputType": "CompletionDate",
                  "completionDate": "2022-04-28T08:18:41.576372Z"
                },
                {
                  "outputType": "FileSize",
                  "sizeBytes": null
                },
                {
                  "outputType": "Count",
                  "max": null,
                  "count": null
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": "angela",
                "email": "angela.martin@dunder-mifflin.com"
              },
              "outputs": [
                {
                  "outputType": "CompletionState",
                  "status": "Unstarted",
                  "isOverridden": false
                },
                {
                  "outputType": "CompletionDate",
                  "completionDate": null
                },
                {
                  "outputType": "FileSize",
                  "sizeBytes": null
                },
                {
                  "outputType": "Count",
                  "max": null,
                  "count": null
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": "creed.bratton",
                "email": "creed.bratton@dunder-mifflin.com"
              },
              "outputs": [
                {
                  "outputType": "CompletionState",
                  "status": "Finished",
                  "isOverridden": false
                },
                {
                  "outputType": "CompletionDate",
                  "completionDate": "2022-04-28T08:18:41.576372Z"
                },
                {
                  "outputType": "FileSize",
                  "sizeBytes": 8284362
                },
                {
                  "outputType": "Count",
                  "max": null,
                  "count": null
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": "erin.hannon",
                "email": "erin.hannon@dunder-mifflin.com"
              },
              "outputs": [
                {
                  "outputType": "CompletionState",
                  "status": "Finished",
                  "isOverridden": false
                },
                {
                  "outputType": "CompletionDate",
                  "completionDate": "2022-04-28T08:18:41.576372Z"
                },
                {
                  "outputType": "FileSize",
                  "sizeBytes": 678768
                },
                {
                  "outputType": "Count",
                  "max": null,
                  "count": null
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": "jim",
                "email": "jim@dunder-mifflin.com"
              },
              "outputs": [
                {
                  "outputType": "CompletionState",
                  "status": "Unstarted",
                  "isOverridden": false
                },
                {
                  "outputType": "CompletionDate",
                  "completionDate": null
                },
                {
                  "outputType": "FileSize",
                  "sizeBytes": null
                },
                {
                  "outputType": "Count",
                  "max": null,
                  "count": null
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": "karen",
                "email": "karen.filipelli@dunder-mifflin.com"
              },
              "outputs": [
                {
                  "outputType": "CompletionState",
                  "status": "Finished",
                  "isOverridden": false
                },
                {
                  "outputType": "CompletionDate",
                  "completionDate": "2022-04-28T08:18:41.576372Z"
                },
                {
                  "outputType": "FileSize",
                  "sizeBytes": null
                },
                {
                  "outputType": "Count",
                  "max": null,
                  "count": 13
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": "kelly",
                "email": "kelly.kapoor@dunder-mifflin.com"
              },
              "outputs": [
                {
                  "outputType": "CompletionState",
                  "status": "Unstarted",
                  "isOverridden": false
                },
                {
                  "outputType": "CompletionDate",
                  "completionDate": null
                },
                {
                  "outputType": "FileSize",
                  "sizeBytes": null
                },
                {
                  "outputType": "Count",
                  "max": null,
                  "count": null
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": "kevin",
                "email": "kevin.mallone@dunder-mifflin.com"
              },
              "outputs": [
                {
                  "outputType": "CompletionState",
                  "status": "Finished",
                  "isOverridden": false
                },
                {
                  "outputType": "CompletionDate",
                  "completionDate": "2022-04-28T08:18:41.576372Z"
                },
                {
                  "outputType": "FileSize",
                  "sizeBytes": 859560
                },
                {
                  "outputType": "Count",
                  "max": null,
                  "count": null
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": "meredith",
                "email": "meredith.palmer@dunder-mifflin.com"
              },
              "outputs": [
                {
                  "outputType": "CompletionState",
                  "status": "Unstarted",
                  "isOverridden": false
                },
                {
                  "outputType": "CompletionDate",
                  "completionDate": null
                },
                {
                  "outputType": "FileSize",
                  "sizeBytes": null
                },
                {
                  "outputType": "Count",
                  "max": null,
                  "count": null
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": "oscar",
                "email": "oscar.martinez@dunder-mifflin.com"
              },
              "outputs": [
                {
                  "outputType": "CompletionState",
                  "status": "Unstarted",
                  "isOverridden": false
                },
                {
                  "outputType": "CompletionDate",
                  "completionDate": null
                },
                {
                  "outputType": "FileSize",
                  "sizeBytes": null
                },
                {
                  "outputType": "Count",
                  "max": null,
                  "count": null
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": "pam",
                "email": "pam@dunder-mifflin.com"
              },
              "outputs": [
                {
                  "outputType": "CompletionState",
                  "status": "Finished",
                  "isOverridden": false
                },
                {
                  "outputType": "CompletionDate",
                  "completionDate": "2022-04-28T08:18:41.576372Z"
                },
                {
                  "outputType": "FileSize",
                  "sizeBytes": 1167
                },
                {
                  "outputType": "Count",
                  "max": null,
                  "count": null
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": "phyllis",
                "email": "phyllis.vance@dunder-mifflin.com"
              },
              "outputs": [
                {
                  "outputType": "CompletionState",
                  "status": "Unstarted",
                  "isOverridden": false
                },
                {
                  "outputType": "CompletionDate",
                  "completionDate": null
                },
                {
                  "outputType": "FileSize",
                  "sizeBytes": null
                },
                {
                  "outputType": "Count",
                  "max": null,
                  "count": null
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": "ryan",
                "email": "ryan.howard@dunder-mifflin.com"
              },
              "outputs": [
                {
                  "outputType": "CompletionState",
                  "status": "Finished",
                  "isOverridden": false
                },
                {
                  "outputType": "CompletionDate",
                  "completionDate": "2022-04-28T08:18:41.576372Z"
                },
                {
                  "outputType": "FileSize",
                  "sizeBytes": null
                },
                {
                  "outputType": "Count",
                  "max": null,
                  "count": null
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": "stanley",
                "email": "stanley@dunder-mifflin.com"
              },
              "outputs": [
                {
                  "outputType": "CompletionState",
                  "status": "Unstarted",
                  "isOverridden": false
                },
                {
                  "outputType": "CompletionDate",
                  "completionDate": null
                },
                {
                  "outputType": "FileSize",
                  "sizeBytes": null
                },
                {
                  "outputType": "Count",
                  "max": null,
                  "count": null
                }
              ]
            }
          },
          {
            "node": {
              "user": {
                "username": "toby",
                "email": "toby.flenderson@dunder-mifflin.com"
              },
              "outputs": [
                {
                  "outputType": "CompletionState",
                  "status": "Finished",
                  "isOverridden": false
                },
                {
                  "outputType": "CompletionDate",
                  "completionDate": "2022-04-28T08:18:41.576372Z"
                },
                {
                  "outputType": "FileSize",
                  "sizeBytes": 529224
                },
                {
                  "outputType": "Count",
                  "max": null,
                  "count": null
                }
              ]
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}


# vs Peergrade

What's new in Eduflow?

Eduflow is successor to our earlier Educational peergrading platform, [Peergrade](https://www.peergrade.io).

## Similarities

| Peergrade (old)   | Eduflow (new)                                                           |
| ----------------- | ----------------------------------------------------------------------- |
| Institution       | Same                                                                    |
| Submission        | Same                                                                    |
| Review / Feedback | Same                                                                    |
| Reaction          | Feedback Reflection                                                     |
| Rubric            | Same + Reflection rubrics customizable                                  |
| Course            | <p>Similar: Contain students and class structure<br></p>                |
| Assignment        | <p>Peer review flow<br>(Submission, Review and Reflection activity)</p> |

## Courses

Peergrade: Contains assignments

Eduflow: Contains flows (unlimited activities per assignment)

## Workflow

In Peergrade, Homework assignments would have a submission and feedback deadline.

In Eduflow, there is no fixed concept of assignment, instead activities are stitched together. Each activity can optionally have its own deadlines set (giving the equivalent of a Homework assignment). Not configuring any deadline offers students an experience similar to live sessions, where they can proceed immediately from submissions to peer reviewing.

Instructor review: While this existed in Peergrade, it wasn't separated fully from peer reviews. Overriding an answer would overwrite all reviewer's scores of that student. Instructor reviews would also impact an [evaluation accuracy score](https://help.peergrade.io/en/articles/2578748-how-feedback-scores-were-calculated-in-scoring-2-1), which has no counterpart in Eduflow.

Unlimited extra reviews: Students can continue to review beyond the minimum value set. This is good for assuring late submitters can have a classmate to review them!


# API Changelog

Looking for general Eduflow updates? Check out Eduflow's [Canny changelog](https://eduflow.canny.io/changelog) to see our latest activities. For upcoming changes, see our [Roadmap](https://eduflow.canny.io/).

### 2022-06-12

[Quizzes](/guides/quizzes) are now available in the QuizActivity data.

### 2022-03-03

[Course](/guides/courses) invite links added to `Course.joinUrl`. This returns the link based on sharing settings in the [course participants page](/guides/courses).

### 2021-08-27

[Tags](/guides/tags) are now available via the `Course.tags` and `Participant.tags` fields and tag operations are available via the `createTag` and `addStudentsToTag` mutations.

{% content-ref url="/pages/-Mi71kk6CPH9yyi1YNqz" %}
[Tags](/guides/tags)
{% endcontent-ref %}

### 2021-07-13

[Discussions](https://docs.eduflow.com/guides/discussions) are now available via [Connections](https://graphql.org/learn/pagination/):

{% tabs %}
{% tab title="Query" %}

```graphql
{
  activity(id: "ae8f3183-0fd0-48e1-b52d-2f455cd78db7") {
    ... on DiscussionActivity {
      title
      topics(first: 2) {
        edges {
          node {
            title
            content
            likesCount
            author {
              name
            }
            commentsCount
            comments {
              edges {
                node {
                  content
                  likesCount
                  repliesCount
                  author {
                    name
                  }
                  replies {
                    edges {
                      node {
                        content
                        author {
                          name
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
```

{% endtab %}

{% tab title="Response" %}

```javascript
{
    'data': {
        'activity': {
            'topics': {
                'totalCount': 2,
                'edges': [
                    {
                        'node': {
                            'title': 'Topic 2',
                            'content': '<p>Topic 2</p>',
                            'author': {
                                'name': 'Ryan Howard'
                            },
                            'commentsCount': 0,
                            'comments': {
                                'edges': [
                                ]
                            },
                            'likesCount': 0
                        }
                    },
                    {
                        'node': {
                            'title': 'Topic 1',
                            'content': '<p>Topic 1</p>',
                            'author': {
                                'name': 'Ryan Howard'
                            },
                            'likesCount': 2,
                            'commentsCount': 4,
                            'comments': {
                                'edges': [
                                    {
                                        'node': {
                                            'author': {
                                                'name': 'Ryan Howard'
                                            },
                                            'content': '<p>Comment 1</p>',
                                            'likesCount': 2,
                                            'replies': {
                                                'edges': [
                                                    {
                                                        'node': {
                                                            'author': {
                                                                'name': 'Ryan Howard'
                                                            },
                                                            'content': '<p>Reply 1</p>'
                                                        }
                                                    },
                                                    {
                                                        'node': {
                                                            'author': {
                                                                'name': 'Ryan Howard'
                                                            },
                                                            'content': '<p>Reply 2</p>'
                                                        }
                                                    }
                                                ]
                                            },
                                            'repliesCount': 2
                                        }
                                    },
                                    {
                                        'node': {
                                            'author': {
                                                'name': 'Pam Beesley'
                                            },
                                            'content': '<p>Comment 2</p>',
                                            'likesCount': 0,
                                            'replies': {
                                                'edges': [
                                                ]
                                            },
                                            'repliesCount': 0
                                        }
                                    }
                                ]
                            }
                        }
                    }
                ]
            }
        }
    }
}
```

{% endtab %}
{% endtabs %}

### 2020-01-20

[Reviews and reflections](/guides/reviews-and-reflections) are now available via [Connections](https://graphql.org/learn/pagination/):

{% tabs %}
{% tab title="GraphQL" %}

```graphql
{
  activity(id: "<activity_id>") {
    ... on ActivityInterface {
      id
      activityInterfaceTitle: title
      activityType
      ... on TeacherReviewActivity {
        reviews(first: 2) {
          pageInfo {
            endCursor
          }
          edges {
            node {
              receivers {
                email
              }
              answers {
                question {
                  id
                }
                textAnswer
              }
            }
          }
        }
      }
    }
  }
}
```

{% endtab %}

{% tab title="Results" %}

```javascript
{
  "data": {
    "activity": {
      "id": "<activity_id>",
      "activityInterfaceTitle": "Flow 1 - Activity 5 - Teacher's Perspective",
      "activityType": "TeacherReview",
      "reviews": {
        "pageInfo": {
          "endCursor": "YXJyYXljb25uZWN0aW9uOjE="
        },
        "edges": [
          {
            "node": {
              "receivers": [
                {
                  "email": "donald@example.com"
                }
              ],
              "answers": [
                {
                  "question": {
                    "id": "397df269-3b17-42fa-bf1d-76ca509c0525"
                  },
                  "textAnswer": "Some awesome answer for a text question"
                },
                {
                  "question": {
                    "id": "43d53089-9577-4312-ac43-2b15ffaa8492"
                  },
                  "textAnswer": null
                },
                {
                  "question": {
                    "id": "760ba06f-26da-4aa4-96bb-7c1fffe1e7a1"
                  },
                  "textAnswer": null
                }
              ]
            }
          },
          {
            "node": {
              "receivers": [
                {
                  "email": null
                }
              ],
              "answers": [
                {
                  "question": {
                    "id": "397df269-3b17-42fa-bf1d-76ca509c0525"
                  },
                  "textAnswer": "Another answer"
                },
                {
                  "question": {
                    "id": "43d53089-9577-4312-ac43-2b15ffaa8492"
                  },
                  "textAnswer": null
                },
                {
                  "question": {
                    "id": "760ba06f-26da-4aa4-96bb-7c1fffe1e7a1"
                  },
                  "textAnswer": null
                }
              ]
            }
          }
        ]
      }
    }
  }
}
```

{% endtab %}
{% endtabs %}


