{
  "openapi": "3.0.3",
  "info": {
    "title": "LTIAAS Connect",
    "version": "2.0",
    "license": {
      "name": "BSD-3-Clause",
      "url": "https://opensource.org/license/bsd-3-clause"
    },
    "description": "The **LTIAAS Connect** API lets a learning management system consume LTI® 1.3 Advantage tools. LTIAAS acts as the LTI® platform on your behalf: you make plain REST calls, and LTIAAS performs the protocol work of launching, authenticating and servicing third-party tools.\n\n:::info\nThis is the API reference for **LTIAAS Connect**. If you are building a *tool* that receives launches from an LMS, you want the [LTIAAS Launch API reference](/api/ltiaas) instead.\n:::\n\n## The three surfaces\n\nConnect has three distinct groups of endpoints, and it helps to know which is which before you start:\n\n1. **Endpoints you call.** [Launch](/connect/api/launch-core-form), [ID Token](/connect/api/idtoken-core-form) and [Tools](/connect/api/tools-create) are ordinary REST endpoints on your LTIAAS subdomain, authenticated with your API key. These are the ones you write code against.\n2. **Endpoints the tool calls.** [Platform Endpoints](/connect/api/lti-authenticate) are the LTI® protocol endpoints that LTIAAS exposes *to* the registered tool. You never call them — you hand their URLs to the tool vendor during registration. They are documented here so you can see exactly what the tool will do.\n3. **Requests LTIAAS makes to you.** [Service Requests](/connect/api/memberships-get) are callbacks. When a tool asks for a roster or writes a grade, LTIAAS forwards that request to the single Service URL you configure in the portal. Your server answers them.\n\n## Getting started\n\nStart with [Introduction to LTIAAS Connect](/connect/guides/introduction) for the concepts, then [Your first launch](/connect/guides/flows/core-launch) for a worked end-to-end example.\n\n## Authenticating\n\nEvery endpoint you call takes your account API key as a bearer token:\n\n```\nAuthorization: Bearer <API_KEY>\n```\n\nSee [Authenticating API requests](/connect/guides/api/authentication) for details, and for how to verify the JWT that LTIAAS sends *you*.\n\n:::caution\nThese endpoints must only be called from your back-end. Requests include your API key, so calling them from a browser would leak your credentials.\n:::"
  },
  "servers": [
    {
      "url": "https://your.ltiaas.com"
    }
  ],
  "tags": [
    {
      "name": "Launch",
      "description": "Start an LTI® launch into a registered tool"
    },
    {
      "name": "ID Token",
      "description": "Complete a launch by describing the user and context to the tool"
    },
    {
      "name": "Tools",
      "description": "Register and manage the tools your LMS can launch"
    },
    {
      "name": "Platform Endpoints",
      "description": "The LTI® protocol endpoints LTIAAS exposes to registered tools"
    },
    {
      "name": "Service Requests",
      "description": "Requests LTIAAS makes to your Service URL on behalf of a tool"
    }
  ],
  "security": [
    {
      "Bearer-Token": []
    }
  ],
  "paths": {
    "/api/launch/core": {
      "post": {
        "summary": "Create Launch Parameters",
        "operationId": "launch-core",
        "tags": [
          "Launch"
        ],
        "description": "Builds the parameters needed to start a resource link launch (`LtiResourceLinkRequest`) into a registered tool.\n\nUse this variant when you want to build the launch form yourself. If you would rather have LTIAAS build it for you, use [Create Launch Form](/connect/api/launch-core-form) instead — it is the same request with a ready-to-render HTML form in the response.\n\nThe returned `ltiMessageHint` is a signed token that represents this launch. It **expires one hour after it is issued**, so the launch must be started within that window.\n\n:::note\n`context` and `resource` are opaque strings here — LTIAAS just carries them through the launch and hands them back to you in the validation request. They become full objects later, on [Complete Launch](/connect/api/idtoken-core).\n:::",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoreLaunchRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Launch parameters created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LaunchParameters"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/UnregisteredTool"
          }
        }
      }
    },
    "/api/launch/core/form": {
      "post": {
        "summary": "Create Launch Form",
        "operationId": "launch-core-form",
        "tags": [
          "Launch"
        ],
        "description": "Builds a self-submitting HTML form that starts a resource link launch (`LtiResourceLinkRequest`) into a registered tool.\n\nThis is the variant most integrations use. Render the returned `form` string in an iframe or a new tab and the browser posts itself into the tool, which begins the OIDC handshake with LTIAAS.\n\nThe request body is identical to [Create Launch Parameters](/connect/api/launch-core); only the response differs.\n\n:::tip\nA common pattern is `\"data:text/html,\" + encodeURIComponent(form)` as an iframe `src`. See [Your first launch](/connect/guides/flows/core-launch) for a complete example.\n:::",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoreLaunchRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Launch form created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FormResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/UnregisteredTool"
          }
        }
      }
    },
    "/api/launch/deeplinking": {
      "post": {
        "summary": "Create Deep Linking Launch Parameters",
        "operationId": "launch-deeplinking",
        "tags": [
          "Launch"
        ],
        "description": "Builds the parameters needed to start a deep linking launch (`LtiDeepLinkingRequest`), which opens the tool's content picker so a teacher can choose what to embed.\n\nIdentical to [Create Launch Parameters](/connect/api/launch-core) except that there is **no `resource` field** — the resource is what the teacher is about to pick. LTIAAS launches to the tool's `deeplinkingEndpoint`, falling back to its `launchEndpoint` if none was registered.\n\nWhen the teacher finishes choosing, the tool posts its selection back to LTIAAS, which forwards it to your Service URL as a [DEEP_LINKING_RESPONSE](/connect/api/deep-linking-response) service request.\n\n:::caution\nDeep linking must be enabled on your account. If it is not, this endpoint returns `403 INACTIVE_SERVICE`.\n:::",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeepLinkingLaunchRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Launch parameters created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LaunchParameters"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InactiveService"
          },
          "404": {
            "$ref": "#/components/responses/UnregisteredTool"
          }
        }
      }
    },
    "/api/launch/deeplinking/form": {
      "post": {
        "summary": "Create Deep Linking Launch Form",
        "operationId": "launch-deeplinking-form",
        "tags": [
          "Launch"
        ],
        "description": "Builds a self-submitting HTML form that starts a deep linking launch (`LtiDeepLinkingRequest`) into a registered tool.\n\nThe request body is identical to [Create Deep Linking Launch Parameters](/connect/api/launch-deeplinking); only the response differs. See the [Deep linking flow](/connect/guides/flows/deep-linking) guide for the full picture.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeepLinkingLaunchRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Launch form created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FormResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InactiveService"
          },
          "404": {
            "$ref": "#/components/responses/UnregisteredTool"
          }
        }
      }
    },
    "/api/idtoken/core": {
      "post": {
        "summary": "Complete Launch",
        "operationId": "idtoken-core",
        "tags": [
          "ID Token"
        ],
        "description": "Completes a resource link launch by telling the tool who the user is and what they are launching.\n\nCall this from the **Launch URL** you configured in the portal, after LTIAAS redirects the browser there with a signed `payload` and you have verified it. The `metadata` value comes from that verified payload.\n\nThis variant returns the raw pieces (`target`, `state`, `idtoken`) so you can build the form yourself. Most integrations want [Complete Launch Form](/connect/api/idtoken-core-form) instead.\n\n:::caution\nThe `metadata` value **expires ten minutes** after the launch begins. If your validation step is slow — waiting on a user login, for example — the launch will fail with `400 INVALID_METADATA_PARAMETER`.\n:::\n\n:::note\nUnlike the launch request, `context` and `resource` here are **objects**, not strings. This trips people up.\n:::",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoreIdTokenRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "ID Token created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IdTokenParameters"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/api/idtoken/core/form": {
      "post": {
        "summary": "Complete Launch Form",
        "operationId": "idtoken-core-form",
        "tags": [
          "ID Token"
        ],
        "description": "Completes a resource link launch and returns a self-submitting HTML form that posts the signed ID Token to the tool.\n\nReturn the `form` string as the body of your Launch URL response and the browser lands in the tool, logged in. The request body is identical to [Complete Launch](/connect/api/idtoken-core).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CoreIdTokenRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "ID Token form created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FormResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/api/idtoken/deeplinking": {
      "post": {
        "summary": "Complete Deep Linking Launch",
        "operationId": "idtoken-deeplinking",
        "tags": [
          "ID Token"
        ],
        "description": "Completes a deep linking launch, sending the user and context to the tool so it can render its content picker.\n\nCall this from the **Deep Linking URL** you configured in the portal. Identical to [Complete Launch](/connect/api/idtoken-core) except that there is **no `resource` object** — nothing has been picked yet.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeepLinkingIdTokenRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "ID Token created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IdTokenParameters"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InactiveService"
          }
        }
      }
    },
    "/api/idtoken/deeplinking/form": {
      "post": {
        "summary": "Complete Deep Linking Launch Form",
        "operationId": "idtoken-deeplinking-form",
        "tags": [
          "ID Token"
        ],
        "description": "Completes a deep linking launch and returns a self-submitting HTML form that posts the signed ID Token to the tool's content picker.\n\nThe request body is identical to [Complete Deep Linking Launch](/connect/api/idtoken-deeplinking).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeepLinkingIdTokenRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "ID Token form created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FormResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InactiveService"
          }
        }
      }
    },
    "/admin/tools": {
      "get": {
        "summary": "Get All Tools",
        "operationId": "tools-list",
        "tags": [
          "Tools"
        ],
        "description": "Returns every tool registered on your account.\n\n:::note\nTool management lives under `/admin`, not `/api`. It uses the same API key.\n:::",
        "parameters": [
          {
            "name": "active",
            "in": "query",
            "required": false,
            "description": "Return only active or only inactive tools. Accepts `true`/`false` as well as `1`/`0`.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Registered tools",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Tool"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "summary": "Register A Tool",
        "operationId": "tools-create",
        "tags": [
          "Tools"
        ],
        "description": "Registers a third-party LTI® 1.3 tool so your LMS can launch it.\n\nLTIAAS generates a `clientId`, a `deploymentId` and a dedicated RSA key pair for the tool, then publishes that key in your account's [keyset](/connect/api/lti-keys). Hand the tool vendor the values listed in [Registering a tool](/connect/guides/starting/registering-a-tool).\n\nThe same thing can be done from the LTIAAS Portal if you would rather not script it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ToolCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Tool registered",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tool"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/admin/tools/{toolId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/toolId"
        }
      ],
      "get": {
        "summary": "Get A Tool",
        "operationId": "tools-get",
        "tags": [
          "Tools"
        ],
        "description": "Returns a single tool registration, including the public key LTIAAS signs its ID Tokens with.",
        "responses": {
          "200": {
            "description": "Tool registration",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tool"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/UnregisteredTool"
          }
        }
      },
      "put": {
        "summary": "Update A Tool",
        "operationId": "tools-update",
        "tags": [
          "Tools"
        ],
        "description": "Updates a tool registration. Every field is optional — anything you omit keeps its current value.\n\n:::note\n`PUT` and `PATCH` behave identically here: both are partial updates. Neither one clears fields you leave out.\n:::",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ToolUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tool updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tool"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/UnregisteredTool"
          }
        }
      },
      "patch": {
        "summary": "Partially Update A Tool",
        "operationId": "tools-patch",
        "tags": [
          "Tools"
        ],
        "description": "Partially updates a tool registration. Identical in behaviour to [Update A Tool](/connect/api/tools-update).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ToolUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Tool updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tool"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/UnregisteredTool"
          }
        }
      },
      "delete": {
        "summary": "Delete A Tool",
        "operationId": "tools-delete",
        "tags": [
          "Tools"
        ],
        "description": "Permanently removes a tool registration and its key pair. Launches referencing this tool will fail afterwards.\n\n:::tip\nIf you only want to stop launches temporarily, [deactivate](/connect/api/tools-deactivate) the tool instead — deactivation is reversible, deletion is not.\n:::",
        "responses": {
          "204": {
            "description": "Tool deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/UnregisteredTool"
          }
        }
      }
    },
    "/admin/tools/{toolId}/activate": {
      "parameters": [
        {
          "$ref": "#/components/parameters/toolId"
        }
      ],
      "post": {
        "summary": "Activate A Tool",
        "operationId": "tools-activate",
        "tags": [
          "Tools"
        ],
        "description": "Marks a tool as active so it can receive launches again.",
        "responses": {
          "200": {
            "description": "Tool activated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tool"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/UnregisteredTool"
          }
        }
      }
    },
    "/admin/tools/{toolId}/deactivate": {
      "parameters": [
        {
          "$ref": "#/components/parameters/toolId"
        }
      ],
      "post": {
        "summary": "Deactivate A Tool",
        "operationId": "tools-deactivate",
        "tags": [
          "Tools"
        ],
        "description": "Marks a tool as inactive. The registration and its keys are kept, but launches are refused with `403 INACTIVE_TOOL` until you activate it again.",
        "responses": {
          "200": {
            "description": "Tool deactivated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tool"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/UnregisteredTool"
          }
        }
      }
    },
    "/lti/authenticate": {
      "get": {
        "summary": "OIDC Authentication Endpoint",
        "operationId": "lti-authenticate",
        "tags": [
          "Platform Endpoints"
        ],
        "security": [],
        "description": "The OIDC authorization endpoint that registered tools call to begin a launch. **You never call this** — hand its URL to the tool vendor as the *authentication* or *authorization* endpoint.\n\nLTIAAS validates the tool's request, then **302-redirects the browser** to the Launch URL or Deep Linking URL you configured in the portal, with a signed `payload` query parameter. Verifying that payload and answering with an [ID Token](/connect/api/idtoken-core-form) is your part of the flow.\n\nBoth `GET` and `POST` are accepted; parameters may arrive in the query string or the form body.\n\n:::note\nThis endpoint is unauthenticated by design. It is protected by the tool's registered redirect URIs, a signed message hint, and a 30-second nonce replay window.\n:::",
        "parameters": [
          {
            "name": "client_id",
            "in": "query",
            "required": true,
            "description": "The tool's client ID, issued by LTIAAS at registration.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "login_hint",
            "in": "query",
            "required": true,
            "description": "Echoed back from the launch — the `user` value you supplied.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lti_message_hint",
            "in": "query",
            "required": true,
            "description": "The signed launch token issued by [Create Launch Parameters](/connect/api/launch-core). Expires one hour after issue.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "redirect_uri",
            "in": "query",
            "required": true,
            "description": "Must match one of the tool's registered `redirectionUris`.",
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "scope",
            "in": "query",
            "required": true,
            "description": "Must be `openid`.",
            "schema": {
              "type": "string",
              "enum": [
                "openid"
              ]
            }
          },
          {
            "name": "response_type",
            "in": "query",
            "required": true,
            "description": "Must be `id_token`.",
            "schema": {
              "type": "string",
              "enum": [
                "id_token"
              ]
            }
          },
          {
            "name": "response_mode",
            "in": "query",
            "required": true,
            "description": "Must be `form_post`.",
            "schema": {
              "type": "string",
              "enum": [
                "form_post"
              ]
            }
          },
          {
            "name": "prompt",
            "in": "query",
            "required": true,
            "description": "Must be `none`.",
            "schema": {
              "type": "string",
              "enum": [
                "none"
              ]
            }
          },
          {
            "name": "state",
            "in": "query",
            "required": true,
            "description": "Opaque value the tool uses to correlate the response.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "nonce",
            "in": "query",
            "required": true,
            "description": "Single-use value. Reusing one within 30 seconds fails with `DUPLICATE_NONCE`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to your configured Launch URL or Deep Linking URL, carrying `?payload=<JWT>`."
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/UnregisteredTool"
          }
        }
      },
      "post": {
        "summary": "OIDC Authentication Endpoint (POST)",
        "operationId": "lti-authenticate-post",
        "tags": [
          "Platform Endpoints"
        ],
        "security": [],
        "description": "Identical to [the GET form](/connect/api/lti-authenticate), for tools that submit the authorization request as a form post. Parameters arrive in the request body instead of the query string.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "client_id",
                  "login_hint",
                  "lti_message_hint",
                  "redirect_uri",
                  "scope",
                  "response_type",
                  "response_mode",
                  "prompt",
                  "state",
                  "nonce"
                ],
                "properties": {
                  "client_id": {
                    "type": "string"
                  },
                  "login_hint": {
                    "type": "string"
                  },
                  "lti_message_hint": {
                    "type": "string"
                  },
                  "redirect_uri": {
                    "type": "string",
                    "format": "uri"
                  },
                  "scope": {
                    "type": "string",
                    "enum": [
                      "openid"
                    ]
                  },
                  "response_type": {
                    "type": "string",
                    "enum": [
                      "id_token"
                    ]
                  },
                  "response_mode": {
                    "type": "string",
                    "enum": [
                      "form_post"
                    ]
                  },
                  "prompt": {
                    "type": "string",
                    "enum": [
                      "none"
                    ]
                  },
                  "state": {
                    "type": "string"
                  },
                  "nonce": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "302": {
            "description": "Redirect to your configured Launch URL or Deep Linking URL, carrying `?payload=<JWT>`."
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/UnregisteredTool"
          }
        }
      }
    },
    "/lti/authorize": {
      "post": {
        "summary": "OAuth2 Access Token Endpoint",
        "operationId": "lti-authorize",
        "tags": [
          "Platform Endpoints"
        ],
        "security": [],
        "description": "The OAuth2 token endpoint tools use to obtain an access token before calling the Names & Roles or Assignment & Grades services. **You never call this** — hand its URL to the tool vendor.\n\nLTIAAS verifies the tool's signed client assertion against its registered `authConfig`, checks that every requested scope is in the tool's `permissions`, and issues a token valid for one hour.",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "grant_type",
                  "client_assertion_type",
                  "client_assertion",
                  "scope"
                ],
                "properties": {
                  "grant_type": {
                    "type": "string",
                    "enum": [
                      "client_credentials"
                    ]
                  },
                  "client_assertion_type": {
                    "type": "string",
                    "enum": [
                      "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
                    ]
                  },
                  "client_assertion": {
                    "type": "string",
                    "description": "A JWT signed by the tool. Its `sub` must be the tool's client ID and its `aud` must include `https://your.ltiaas.com/lti/authorize`."
                  },
                  "scope": {
                    "type": "string",
                    "description": "Space-separated list of requested LTI® service scope URLs."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Access token issued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "access_token": {
                      "type": "string"
                    },
                    "token_type": {
                      "type": "string",
                      "example": "bearer"
                    },
                    "scope": {
                      "type": "string",
                      "description": "Space-separated list of granted scope URLs."
                    },
                    "expires_in": {
                      "type": "integer",
                      "example": 3600
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/lti/keys": {
      "get": {
        "summary": "Keyset",
        "operationId": "lti-keys",
        "tags": [
          "Platform Endpoints"
        ],
        "security": [],
        "description": "Your account's public JWKS. Tools fetch this to verify the ID Tokens LTIAAS signs on your behalf. **You never call this** — hand its URL to the tool vendor as the keyset or JWKS URL.\n\nThe set contains one key per registered tool, each with its `kid` set to that tool's ID. An account with no registered tools returns an empty `keys` array rather than a 404.",
        "responses": {
          "200": {
            "description": "JSON Web Key Set",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "kty": {
                            "type": "string",
                            "example": "RSA"
                          },
                          "kid": {
                            "type": "string",
                            "description": "The tool's ID."
                          },
                          "alg": {
                            "type": "string",
                            "example": "RS256"
                          },
                          "use": {
                            "type": "string",
                            "example": "sig"
                          },
                          "n": {
                            "type": "string"
                          },
                          "e": {
                            "type": "string",
                            "example": "AQAB"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/lti/deeplinking/return/{contextId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/contextId"
        }
      ],
      "post": {
        "summary": "Deep Linking Return",
        "operationId": "lti-deeplinking-return",
        "tags": [
          "Platform Endpoints"
        ],
        "security": [],
        "description": "Where a tool posts the content a teacher selected. **You never call this** — LTIAAS advertises its URL to the tool inside the deep linking ID Token.\n\nLTIAAS verifies the tool's signed response, then relays the selection to your Service URL as a [DEEP_LINKING_RESPONSE](/connect/api/deep-linking-response) service request. The reply is an HTML page that submits itself, so the browser carries the selection to your server.\n\n:::caution\nOnly `ltiResourceLink` content items are accepted. Any other type fails with `400 UNSUPPORTED_CONTENT_ITEM_TYPE`.\n:::",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "JWT"
                ],
                "properties": {
                  "JWT": {
                    "type": "string",
                    "description": "The tool's signed deep linking response."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A self-submitting HTML form that forwards the selection to your Service URL.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/lti/memberships/{contextId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/contextId"
        }
      ],
      "get": {
        "summary": "Names And Roles Membership Container",
        "operationId": "lti-memberships",
        "tags": [
          "Platform Endpoints"
        ],
        "security": [
          {
            "Tool-Access-Token": []
          }
        ],
        "description": "The Names & Roles Provisioning Service endpoint tools call to fetch a course roster. **You never call this.**\n\nLTIAAS turns the call into a [MEMBERSHIPS_GET](/connect/api/memberships-get) service request against your Service URL, converts your reply into LTI® wire format, and filters out any personal data the tool is not permitted to see.\n\nRequires an access token with the `MEMBERSHIPS_READ` scope.",
        "responses": {
          "200": {
            "description": "Membership container",
            "content": {
              "application/vnd.ims.lti-nrps.v2.membershipcontainer+json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uri"
                    },
                    "context": {
                      "$ref": "#/components/schemas/ServiceContext"
                    },
                    "members": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "user_id": {
                            "type": "string"
                          },
                          "roles": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "format": "uri"
                            }
                          },
                          "name": {
                            "type": "string"
                          },
                          "given_name": {
                            "type": "string"
                          },
                          "family_name": {
                            "type": "string"
                          },
                          "middle_name": {
                            "type": "string"
                          },
                          "email": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InactiveService"
          }
        }
      }
    },
    "/lti/lineitems/{contextId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/contextId"
        }
      ],
      "get": {
        "summary": "Get Line Items",
        "operationId": "lti-lineitems-list",
        "tags": [
          "Platform Endpoints"
        ],
        "security": [
          {
            "Tool-Access-Token": []
          }
        ],
        "description": "The Assignment & Grades endpoint tools call to list grade lines in a context. **You never call this** — LTIAAS relays it to your Service URL as a [LINEITEMS_GET](/connect/api/lineitems-get) service request.",
        "parameters": [
          {
            "name": "resource_link_id",
            "in": "query",
            "required": false,
            "description": "Only return line items attached to this resource link.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tag",
            "in": "query",
            "required": false,
            "description": "Only return line items carrying this tag.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Line items",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LineItem"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InactiveService"
          }
        }
      },
      "post": {
        "summary": "Create Line Item",
        "operationId": "lti-lineitems-create",
        "tags": [
          "Platform Endpoints"
        ],
        "security": [
          {
            "Tool-Access-Token": []
          }
        ],
        "description": "The Assignment & Grades endpoint tools call to create a grade line. **You never call this** — LTIAAS relays it to your Service URL as a [LINEITEMS_POST](/connect/api/lineitems-post) service request.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LineItemInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Line item created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LineItem"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InactiveService"
          }
        }
      }
    },
    "/lti/lineitems/{contextId}/{lineItemId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/contextId"
        },
        {
          "$ref": "#/components/parameters/lineItemId"
        }
      ],
      "get": {
        "summary": "Get Line Item",
        "operationId": "lti-lineitem-get",
        "tags": [
          "Platform Endpoints"
        ],
        "security": [
          {
            "Tool-Access-Token": []
          }
        ],
        "description": "Relayed to your Service URL as a [LINEITEM_GET](/connect/api/lineitem-get) service request. **You never call this.**",
        "responses": {
          "200": {
            "description": "Line item",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LineItem"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InactiveService"
          }
        }
      },
      "put": {
        "summary": "Update Line Item",
        "operationId": "lti-lineitem-update",
        "tags": [
          "Platform Endpoints"
        ],
        "security": [
          {
            "Tool-Access-Token": []
          }
        ],
        "description": "Relayed to your Service URL as a [LINEITEM_PUT](/connect/api/lineitem-put) service request. **You never call this.**",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LineItemInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Line item updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LineItem"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InactiveService"
          }
        }
      },
      "delete": {
        "summary": "Delete Line Item",
        "operationId": "lti-lineitem-delete",
        "tags": [
          "Platform Endpoints"
        ],
        "security": [
          {
            "Tool-Access-Token": []
          }
        ],
        "description": "Relayed to your Service URL as a [LINEITEM_DELETE](/connect/api/lineitem-delete) service request. **You never call this.**",
        "responses": {
          "204": {
            "description": "Line item deleted"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InactiveService"
          }
        }
      }
    },
    "/lti/lineitems/{contextId}/{lineItemId}/scores": {
      "parameters": [
        {
          "$ref": "#/components/parameters/contextId"
        },
        {
          "$ref": "#/components/parameters/lineItemId"
        }
      ],
      "post": {
        "summary": "Submit Score",
        "operationId": "lti-scores-create",
        "tags": [
          "Platform Endpoints"
        ],
        "security": [
          {
            "Tool-Access-Token": []
          }
        ],
        "description": "The Assignment & Grades endpoint tools call to post a grade. **You never call this** — LTIAAS relays it to your Service URL as a [SCORE_POST](/connect/api/score-post) service request, and passes your status code straight back to the tool.\n\nRequires an access token with the `GRADES_WRITE` scope.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Score"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Score accepted. The exact status is whatever your Service URL returned."
          },
          "400": {
            "$ref": "#/components/responses/ValidationError"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InactiveService"
          }
        }
      }
    },
    "/lti/lineitems/{contextId}/{lineItemId}/results": {
      "parameters": [
        {
          "$ref": "#/components/parameters/contextId"
        },
        {
          "$ref": "#/components/parameters/lineItemId"
        }
      ],
      "get": {
        "summary": "Get Results",
        "operationId": "lti-results-list",
        "tags": [
          "Platform Endpoints"
        ],
        "security": [
          {
            "Tool-Access-Token": []
          }
        ],
        "description": "The Assignment & Grades endpoint tools call to read grades. **You never call this** — LTIAAS relays it to your Service URL as a [RESULTS_GET](/connect/api/results-get) service request.\n\nRequires an access token with the `GRADES_READ` scope.",
        "parameters": [
          {
            "name": "user_id",
            "in": "query",
            "required": false,
            "description": "Only return the result for this user.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Result"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/InactiveService"
          }
        }
      }
    }
  },
  "x-webhooks": {
    "MEMBERSHIPS_GET": {
      "post": {
        "operationId": "memberships-get",
        "summary": "MEMBERSHIPS_GET",
        "tags": [
          "Service Requests"
        ],
        "security": [],
        "description": "Sent when a tool asks for the roster of a course. Return every user in the requested context.\n\nLTIAAS converts your role keys into LTI® role URLs and strips any personal data the tool is not allowed to see, so you can always send complete records.\n\n**Request:** `POST` to your Service URL with a JSON body of `{ \"payload\": \"<JWT>\" }`. Verify the JWT with your consumer public key, then read `parameters`.\n\n**The decoded payload:**\n\n```json\n{\n  \"type\": \"MEMBERSHIPS_GET\",\n  \"parameters\": {\n    \"context\": \"2022CSEa5e6c431b91\",\n    \"clientId\": \"qR8E0iHqSdR30DdfQAbcaBGjKT65\"\n  }\n}\n```",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServiceRequestBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The roster for this context.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "context",
                    "members"
                  ],
                  "properties": {
                    "context": {
                      "$ref": "#/components/schemas/ServiceContext"
                    },
                    "members": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "roles"
                        ],
                        "properties": {
                          "id": {
                            "type": "string",
                            "example": "41"
                          },
                          "roles": {
                            "type": "array",
                            "items": {
                              "$ref": "#/components/schemas/Role"
                            }
                          },
                          "name": {
                            "type": "string",
                            "example": "John Doe"
                          },
                          "givenName": {
                            "type": "string",
                            "example": "John"
                          },
                          "familyName": {
                            "type": "string",
                            "example": "Doe"
                          },
                          "middleName": {
                            "type": "string"
                          },
                          "email": {
                            "type": "string",
                            "example": "john.doe@myschool.com"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "DEEP_LINKING_RESPONSE": {
      "post": {
        "operationId": "deep-linking-response",
        "summary": "DEEP_LINKING_RESPONSE",
        "tags": [
          "Service Requests"
        ],
        "security": [],
        "description": "Sent when a teacher finishes picking content in a tool's deep linking picker. Store the selection against the course, then close the picker.\n\n:::caution\nThis one request arrives **form-urlencoded**, not as JSON — it is delivered by a self-submitting form in the user's browser rather than a server-to-server call. Every other service request uses a JSON body.\n:::\n\nBecause the browser is sitting on this response, a good pattern is to return a small HTML page that posts a message to the parent window and lets it close the iframe. See the [deep linking guide](/connect/guides/flows/deep-linking) for a worked example.\n\nOnly `ltiResourceLink` items are ever present — LTIAAS rejects other types before they reach you.\n\n**The decoded payload:**\n\n```json\n{\n  \"type\": \"DEEP_LINKING_RESPONSE\",\n  \"parameters\": {\n    \"context\": \"2022CSEa5e6c431b91\",\n    \"clientId\": \"qR8E0iHqSdR30DdfQAbcaBGjKT65\",\n    \"contentItems\": [\n      {\n        \"type\": \"ltiResourceLink\",\n        \"url\": \"https://mytool.com/lti/launch?resource=13\",\n        \"title\": \"Resource 13\"\n      }\n    ]\n  }\n}\n```",
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/ServiceRequestBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Any body you like — an empty response is fine, but returning HTML lets you close the picker cleanly.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "LINEITEMS_GET": {
      "post": {
        "operationId": "lineitems-get",
        "summary": "LINEITEMS_GET",
        "tags": [
          "Service Requests"
        ],
        "security": [],
        "description": "Sent when a tool lists the grade lines in a course. Return the line items for the context, honouring `filters` if present.\n\nThe `id` you return is used to build the URL the tool will use for later reads and writes, so it must be stable.\n\n**The decoded payload:**\n\n```json\n{\n  \"type\": \"LINEITEMS_GET\",\n  \"parameters\": {\n    \"context\": \"2022CSEa5e6c431b91\",\n    \"clientId\": \"348080fn9du9b9ufvb92rfb9l\",\n    \"filters\": {\n      \"resourceLinkId\": \"17\",\n      \"tag\": \"myLineItems\"\n    }\n  }\n}\n```",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServiceRequestBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The matching line items.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LineItem"
                  }
                }
              }
            }
          }
        }
      }
    },
    "LINEITEMS_POST": {
      "post": {
        "operationId": "lineitems-post",
        "summary": "LINEITEMS_POST",
        "tags": [
          "Service Requests"
        ],
        "security": [],
        "description": "Sent when a tool creates a grade line. Store it and return the stored record, including the `id` you assigned.\n\n**The decoded payload:**\n\n```json\n{\n  \"type\": \"LINEITEMS_POST\",\n  \"parameters\": {\n    \"context\": \"2022CSEa5e6c431b91\",\n    \"clientId\": \"348080fn9du9b9ufvb92rfb9l\",\n    \"lineItem\": {\n      \"label\": \"My first grade line\",\n      \"scoreMaximum\": 99,\n      \"resourceLinkId\": \"412\",\n      \"tag\": \"new_tag\"\n    }\n  }\n}\n```",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServiceRequestBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The line item you stored.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LineItem"
                }
              }
            }
          }
        }
      }
    },
    "LINEITEM_GET": {
      "post": {
        "operationId": "lineitem-get",
        "summary": "LINEITEM_GET",
        "tags": [
          "Service Requests"
        ],
        "security": [],
        "description": "Sent when a tool reads a single grade line. Return it, or answer with a 4xx if it does not exist — non-2xx bodies are passed through to the tool untouched.\n\n**The decoded payload:**\n\n```json\n{\n  \"type\": \"LINEITEM_GET\",\n  \"parameters\": {\n    \"context\": \"2022CSEa5e6c431b91\",\n    \"clientId\": \"348080fn9du9b9ufvb92rfb9l\",\n    \"lineItemId\": \"412\"\n  }\n}\n```",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServiceRequestBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The requested line item.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LineItem"
                }
              }
            }
          }
        }
      }
    },
    "LINEITEM_PUT": {
      "post": {
        "operationId": "lineitem-put",
        "summary": "LINEITEM_PUT",
        "tags": [
          "Service Requests"
        ],
        "security": [],
        "description": "Sent when a tool updates an existing grade line. Apply the change and return the stored record.\n\n**The decoded payload:**\n\n```json\n{\n  \"type\": \"LINEITEM_PUT\",\n  \"parameters\": {\n    \"context\": \"2022CSEa5e6c431b91\",\n    \"clientId\": \"348080fn9du9b9ufvb92rfb9l\",\n    \"lineItemId\": \"412\",\n    \"lineItem\": {\n      \"label\": \"My first grade line\",\n      \"scoreMaximum\": 99,\n      \"resourceLinkId\": \"412\",\n      \"tag\": \"new_tag\"\n    }\n  }\n}\n```",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServiceRequestBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The updated line item.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LineItem"
                }
              }
            }
          }
        }
      }
    },
    "LINEITEM_DELETE": {
      "post": {
        "operationId": "lineitem-delete",
        "summary": "LINEITEM_DELETE",
        "tags": [
          "Service Requests"
        ],
        "security": [],
        "description": "Sent when a tool deletes a grade line. Remove it and return `200` with an empty body.\n\n**The decoded payload:**\n\n```json\n{\n  \"type\": \"LINEITEM_DELETE\",\n  \"parameters\": {\n    \"context\": \"2022CSEa5e6c431b91\",\n    \"clientId\": \"348080fn9du9b9ufvb92rfb9l\",\n    \"lineItemId\": \"412\"\n  }\n}\n```",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServiceRequestBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Deleted. Return an empty body."
          }
        }
      }
    },
    "SCORE_POST": {
      "post": {
        "operationId": "score-post",
        "summary": "SCORE_POST",
        "tags": [
          "Service Requests"
        ],
        "security": [],
        "description": "Sent when a tool submits a grade for a user. Record it and return `200` with an empty JSON object.\n\nYour status code is passed straight back to the tool, so use a 4xx if you reject the grade.\n\n`scoreGiven` is optional — a score can report progress without a grade — but when it is present, `scoreMaximum` is required alongside it.\n\n**The decoded payload:**\n\n```json\n{\n  \"type\": \"SCORE_POST\",\n  \"parameters\": {\n    \"context\": \"2022CSEa5e6c431b91\",\n    \"clientId\": \"348080fn9du9b9ufvb92rfb9l\",\n    \"lineItemId\": \"412\",\n    \"score\": {\n      \"userId\": \"41\",\n      \"activityProgress\": \"Completed\",\n      \"gradingProgress\": \"FullyGraded\",\n      \"comment\": \"Great work!\",\n      \"scoreGiven\": 99,\n      \"scoreMaximum\": 100\n    }\n  }\n}\n```",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServiceRequestBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recorded. Return an empty JSON object.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "RESULTS_GET": {
      "post": {
        "operationId": "results-get",
        "summary": "RESULTS_GET",
        "tags": [
          "Service Requests"
        ],
        "security": [],
        "description": "Sent when a tool reads the grades recorded against a grade line. Return an array of results, filtered by `filters.userId` when present.\n\n**The decoded payload:**\n\n```json\n{\n  \"type\": \"RESULTS_GET\",\n  \"parameters\": {\n    \"context\": \"2022CSEa5e6c431b91\",\n    \"clientId\": \"348080fn9du9b9ufvb92rfb9l\",\n    \"lineItemId\": \"412\",\n    \"filters\": {\n      \"userId\": \"14\"\n    }\n  }\n}\n```",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ServiceRequestBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The matching results.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Result"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "Bearer-Token": {
        "name": "Authorization",
        "type": "apiKey",
        "in": "header",
        "description": "An http Header whose value is of the format: `Bearer <API_KEY>`. Where <API_KEY> should be replaced with your static API Key, available in the LTIAAS Portal. Learn more in the <a href=\"/connect/guides/api/authentication\">Authenticating API requests</a> guide."
      },
      "Tool-Access-Token": {
        "name": "Authorization",
        "type": "apiKey",
        "in": "header",
        "description": "An http Header whose value is of the format: `Bearer <ACCESS_TOKEN>`, where <ACCESS_TOKEN> was issued by the <a href=\"/connect/api/lti-authorize\">access token endpoint</a>. This is presented by the registered tool, not by you."
      }
    },
    "parameters": {
      "toolId": {
        "name": "toolId",
        "in": "path",
        "required": true,
        "description": "The tool's ID, as returned at registration. This is also the tool's `clientId`.",
        "schema": {
          "type": "string"
        },
        "example": "3wT7bVhUADSsfdG33fVV"
      },
      "contextId": {
        "name": "contextId",
        "in": "path",
        "required": true,
        "description": "The URL-encoded context identifier — the same `context` value you supplied when starting the launch.",
        "schema": {
          "type": "string"
        },
        "example": "2022CSEa5e6c431b91"
      },
      "lineItemId": {
        "name": "lineItemId",
        "in": "path",
        "required": true,
        "description": "The line item identifier your Service URL assigned.",
        "schema": {
          "type": "string"
        },
        "example": "412"
      }
    },
    "schemas": {
      "PersonalData": {
        "type": "string",
        "description": "How much personal data may be sent to the tool.\n\n- `NONE` — no name and no email\n- `EMAIL` — email only\n- `NAME` — full, given and family name\n- `COMPLETE` — name and email",
        "enum": [
          "NONE",
          "EMAIL",
          "NAME",
          "COMPLETE"
        ],
        "example": "COMPLETE"
      },
      "Permission": {
        "type": "string",
        "description": "An LTI® service a tool is allowed to use.",
        "enum": [
          "MEMBERSHIPS_READ",
          "LINEITEMS_READ",
          "LINEITEMS_READ_WRITE",
          "GRADES_READ",
          "GRADES_WRITE",
          "OUTCOMES_READ_WRITE",
          "TOOL_SETTINGS_READ_WRITE"
        ],
        "example": "MEMBERSHIPS_READ"
      },
      "Role": {
        "type": "string",
        "description": "A role key. LTIAAS expands these into the full IMS role URLs before sending them to the tool.",
        "enum": [
          "SYSTEM_ADMINISTRATOR",
          "SYSTEM_NONE",
          "SYSTEM_ACCOUNT_ADMIN",
          "SYSTEM_CRETOR",
          "SYSTEM_SYS_ADMIN",
          "SYSTEM_SYS_SUPPORT",
          "SYSTEM_USER",
          "INSTITUTION_ADMINISTRATOR",
          "INSTITUTION_FACULTY",
          "INSTITUTION_GUEST",
          "INSTITUTION_NONE",
          "INSTITUTION_OTHER",
          "INSTITUTION_STAFF",
          "INSTITUTION_STUDENT",
          "INSTITUTION_ALUMNI",
          "INSTITUTION_INSTRUCTOR",
          "INSTITUTION_LEARNER",
          "INSTITUTION_MEMBER",
          "INSTITUTION_MENTOR",
          "INSTITUTION_OBSERVER",
          "INSTITUTION_PROSPECTIVE",
          "CONTEXT_ADMINISTRATOR",
          "CONTEXT_CONTENT_DEVELOPER",
          "CONTEXT_INSTRUCTOR",
          "CONTEXT_LEARNER",
          "CONTEXT_MENTOR",
          "CONTEXT_MANAGER",
          "CONTEXT_MEMBER",
          "CONTEXT_OFFICER"
        ],
        "example": "CONTEXT_LEARNER"
      },
      "CoreLaunchRequest": {
        "type": "object",
        "required": [
          "clientId",
          "context",
          "resource",
          "user"
        ],
        "properties": {
          "clientId": {
            "type": "string",
            "description": "The tool to launch, identified by the client ID LTIAAS issued when you registered it.",
            "example": "U0gIhEYg2rdIr4ABscEJ"
          },
          "context": {
            "type": "string",
            "description": "Your identifier for the context the launch happens in — usually a course ID. Opaque to LTIAAS; you get it back on the validation request.",
            "example": "0001"
          },
          "resource": {
            "type": "string",
            "description": "Your identifier for this particular placement of the tool inside the context — usually the database ID of the activity. Opaque to LTIAAS.",
            "example": "14113"
          },
          "user": {
            "description": "Your identifier for the user being launched.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer",
                "minimum": 1
              }
            ],
            "example": "rKk4PdLgcRbqE4PdSW3iV0KhAmu2"
          },
          "launchEndpoint": {
            "type": "string",
            "format": "uri",
            "description": "Override the tool's registered launch URL for this launch only. Useful for deep-linked content, where each placement has its own URL.",
            "example": "https://tool.com/lti/launch?resource=123"
          },
          "personalData": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PersonalData"
              }
            ],
            "description": "Override the tool's registered privacy level for this launch only."
          },
          "customParameters": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Extra key/value pairs to send to the tool. Merged over the tool's registered `customParameters`, so anything you set here wins.",
            "example": {
              "myparam": "myvalue",
              "param2": "val2"
            }
          }
        }
      },
      "DeepLinkingLaunchRequest": {
        "type": "object",
        "required": [
          "clientId",
          "context",
          "user"
        ],
        "properties": {
          "clientId": {
            "type": "string",
            "description": "The tool whose content picker should open.",
            "example": "U0gIhEYg2rdIr4ABscEJ"
          },
          "context": {
            "type": "string",
            "description": "Your identifier for the context — usually a course ID.",
            "example": "0001"
          },
          "user": {
            "description": "Your identifier for the user doing the picking, normally a teacher.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer",
                "minimum": 1
              }
            ],
            "example": "rKk4PdLgcRbqE4PdSW3iV0KhAmu2"
          },
          "personalData": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PersonalData"
              }
            ],
            "description": "Override the tool's registered privacy level for this launch only."
          },
          "customParameters": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Extra key/value pairs to send to the tool.",
            "example": {
              "myparam": "myvalue"
            }
          }
        }
      },
      "LaunchParameters": {
        "type": "object",
        "description": "The pieces of an LTI® launch. Post these to `target` as a form to start the launch.",
        "properties": {
          "target": {
            "type": "string",
            "format": "uri",
            "description": "The tool's OIDC login initiation URL. Post the form here.",
            "example": "https://mytool.ltiaas.com/lti/login"
          },
          "iss": {
            "type": "string",
            "format": "uri",
            "description": "Your LTIAAS subdomain, acting as the LTI® issuer.",
            "example": "https://your.ltiaas.com"
          },
          "clientId": {
            "type": "string",
            "example": "U0gIhEYg2rdIr4ABscEJ"
          },
          "ltiDeploymentId": {
            "type": "string",
            "example": "EgAssgssdfgsfshshq8iw"
          },
          "targetLinkUri": {
            "type": "string",
            "format": "uri",
            "description": "Where the tool should land after the handshake.",
            "example": "https://mytool.ltiaas.com/lti/launch"
          },
          "loginHint": {
            "type": "string",
            "description": "The `user` value you supplied.",
            "example": "rKk4PdLgcRbqE4PdSW3iV0KhAmu2"
          },
          "ltiMessageHint": {
            "type": "string",
            "description": "A signed token representing this launch. Expires one hour after issue.",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          }
        }
      },
      "FormResponse": {
        "type": "object",
        "properties": {
          "form": {
            "type": "string",
            "description": "A self-submitting HTML form. Render it in an iframe or a new tab; it posts itself as soon as it loads.",
            "example": "<form id=\"ltiaas_launch\" style=\"display: none;\" action=\"https://mytool.ltiaas.com/lti/login\" method=\"POST\">\n    <input type=\"hidden\" name=\"iss\" value=\"https://your.ltiaas.com\" />\n    <input type=\"hidden\" name=\"client_id\" value=\"U0gIhEYg2rdIr4ABscEJ\" />\n    <input type=\"hidden\" name=\"lti_deployment_id\" value=\"EgAssgssdfgsfshshq8iw\" />\n    <input type=\"hidden\" name=\"target_link_uri\" value=\"https://mytool.ltiaas.com/lti/launch\" />\n    <input type=\"hidden\" name=\"login_hint\" value=\"rKk4PdLgcRbqE4PdSW3iV0KhAmu2\" />\n    <input type=\"hidden\" name=\"lti_message_hint\" value=\"a.long.jwt\" />\n  </form>\n  <script>document.getElementById(\"ltiaas_launch\").submit()</script>"
          }
        }
      },
      "IdTokenUser": {
        "type": "object",
        "required": [
          "id",
          "roles"
        ],
        "properties": {
          "id": {
            "description": "Your identifier for the user. Should match the `user` value you sent on the launch.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer",
                "minimum": 1
              }
            ],
            "example": "rKk4PdLgcRbqE4PdSW3iV0KhAmu2"
          },
          "roles": {
            "type": "array",
            "description": "The user's roles in this context. Many tools change what they show based on whether the user is an instructor or a learner.",
            "items": {
              "$ref": "#/components/schemas/Role"
            },
            "example": [
              "CONTEXT_LEARNER"
            ]
          },
          "email": {
            "type": "string",
            "example": "john.doe@myschool.com"
          },
          "name": {
            "type": "string",
            "example": "John Doe"
          },
          "givenName": {
            "type": "string",
            "example": "John"
          },
          "familyName": {
            "type": "string",
            "example": "Doe"
          }
        },
        "description": "The user being launched. Name and email fields are only forwarded if the tool's privacy level permits it, so it is safe to always send them."
      },
      "IdTokenContext": {
        "type": "object",
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "description": "Your identifier for the context. Should match the `context` value you sent on the launch.",
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer",
                "minimum": 1
              }
            ],
            "example": "0001"
          },
          "label": {
            "type": "string",
            "description": "Short name, such as a course code.",
            "example": "CS101"
          },
          "title": {
            "type": "string",
            "description": "Full name.",
            "example": "Computer Science 101"
          },
          "type": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The kind of context this is. Most integrations send `[\"CourseOffering\"]`.",
            "example": [
              "CourseOffering"
            ]
          }
        }
      },
      "CoreIdTokenRequest": {
        "type": "object",
        "required": [
          "metadata",
          "user",
          "context",
          "resource"
        ],
        "properties": {
          "metadata": {
            "type": "string",
            "description": "The `metadata` claim from the verified `payload` JWT that LTIAAS sent to your Launch URL. Expires ten minutes after the launch begins.",
            "example": "kR2mQ9dLxTn4vB8sYw1e"
          },
          "user": {
            "$ref": "#/components/schemas/IdTokenUser"
          },
          "context": {
            "$ref": "#/components/schemas/IdTokenContext"
          },
          "resource": {
            "type": "object",
            "required": [
              "id"
            ],
            "description": "The activity being launched.",
            "properties": {
              "id": {
                "description": "Your identifier for the placement. Should match the `resource` value you sent on the launch.",
                "oneOf": [
                  {
                    "type": "string"
                  },
                  {
                    "type": "integer",
                    "minimum": 1
                  }
                ],
                "example": "14113"
              },
              "title": {
                "type": "string",
                "example": "A cool learning tool"
              },
              "description": {
                "type": "string",
                "example": "This tool will help you learn as part of the CS101 course."
              }
            }
          }
        }
      },
      "DeepLinkingIdTokenRequest": {
        "type": "object",
        "required": [
          "metadata",
          "user",
          "context"
        ],
        "properties": {
          "metadata": {
            "type": "string",
            "description": "The `metadata` claim from the verified `payload` JWT that LTIAAS sent to your Deep Linking URL.",
            "example": "kR2mQ9dLxTn4vB8sYw1e"
          },
          "user": {
            "$ref": "#/components/schemas/IdTokenUser"
          },
          "context": {
            "$ref": "#/components/schemas/IdTokenContext"
          }
        }
      },
      "IdTokenParameters": {
        "type": "object",
        "description": "The pieces of a completed launch. Post these to `target` as a form.",
        "properties": {
          "target": {
            "type": "string",
            "format": "uri",
            "description": "The tool's redirect URI. Post the form here.",
            "example": "https://mytool.ltiaas.com/lti/launch"
          },
          "state": {
            "type": "string",
            "description": "The tool's original `state` value, echoed back.",
            "example": "aLongString"
          },
          "idtoken": {
            "type": "string",
            "description": "The signed LTI® ID Token describing the user, context and resource.",
            "example": "aVeryLongString"
          }
        }
      },
      "AuthConfig": {
        "type": "object",
        "required": [
          "method",
          "key"
        ],
        "description": "How LTIAAS verifies messages signed by the tool.",
        "properties": {
          "method": {
            "type": "string",
            "enum": [
              "JWK_SET",
              "JWK_KEY",
              "RSA_KEY"
            ],
            "description": "- `JWK_SET` — `key` is a URL to the tool's JWKS. Preferred, because the tool can rotate keys without telling you.\n- `JWK_KEY` — `key` is a single JWK, as a JSON string.\n- `RSA_KEY` — `key` is a PEM-encoded public key.",
            "example": "JWK_SET"
          },
          "key": {
            "type": "string",
            "description": "The keyset URL, JWK or PEM key, depending on `method`.",
            "example": "https://mytool.ltiaas.com/keys"
          }
        }
      },
      "ToolCreate": {
        "type": "object",
        "required": [
          "name",
          "launchEndpoint",
          "loginEndpoint",
          "redirectionUris",
          "permissions",
          "personalData",
          "authConfig"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "A human-readable name for the tool.",
            "example": "My Tool"
          },
          "launchEndpoint": {
            "type": "string",
            "format": "uri",
            "description": "Where resource link launches land by default.",
            "example": "https://mytool.ltiaas.com/lti/launch"
          },
          "loginEndpoint": {
            "type": "string",
            "format": "uri",
            "description": "The tool's OIDC login initiation URL — where LTIAAS posts to start a launch.",
            "example": "https://mytool.ltiaas.com/lti/login"
          },
          "deeplinkingEndpoint": {
            "type": "string",
            "format": "uri",
            "description": "Where deep linking launches land. Falls back to `launchEndpoint`.",
            "example": "https://mytool.ltiaas.com/lti/deeplink"
          },
          "redirectionUris": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            },
            "description": "Every URL the tool is allowed to be redirected back to. A launch whose `redirect_uri` is not on this list is refused.",
            "example": [
              "https://mytool.ltiaas.com/lti/launch"
            ]
          },
          "permissions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Permission"
            },
            "description": "Which LTI® services this tool may use. Requests for any other scope are refused.",
            "example": [
              "MEMBERSHIPS_READ",
              "LINEITEMS_READ"
            ]
          },
          "personalData": {
            "$ref": "#/components/schemas/PersonalData"
          },
          "customParameters": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Key/value pairs sent on every launch of this tool. Individual launches can add to or override these.",
            "example": {}
          },
          "active": {
            "type": "boolean",
            "description": "Whether the tool can receive launches. Defaults to `true`.",
            "default": true,
            "example": true
          },
          "authConfig": {
            "$ref": "#/components/schemas/AuthConfig"
          }
        }
      },
      "ToolUpdate": {
        "type": "object",
        "description": "Every field is optional. Omitted fields keep their current values.",
        "properties": {
          "name": {
            "type": "string",
            "example": "My Tool"
          },
          "launchEndpoint": {
            "type": "string",
            "format": "uri"
          },
          "loginEndpoint": {
            "type": "string",
            "format": "uri"
          },
          "deeplinkingEndpoint": {
            "type": "string",
            "format": "uri"
          },
          "redirectionUris": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "permissions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Permission"
            }
          },
          "personalData": {
            "$ref": "#/components/schemas/PersonalData"
          },
          "customParameters": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "active": {
            "type": "boolean"
          },
          "authConfig": {
            "$ref": "#/components/schemas/AuthConfig"
          }
        }
      },
      "Tool": {
        "type": "object",
        "description": "A registered tool.",
        "properties": {
          "id": {
            "type": "string",
            "example": "3wT7bVhgsve6byG33fVV"
          },
          "clientId": {
            "type": "string",
            "description": "Always equal to `id`. This is the value you pass as `clientId` when launching, and the value the tool knows itself by.",
            "example": "3wT7bVhgsve6byG33fVV"
          },
          "deploymentId": {
            "type": "string",
            "description": "Identifies this deployment of the tool. Generated by LTIAAS at registration.",
            "example": "EgAssgssdfgsfshshq8iw"
          },
          "name": {
            "type": "string",
            "example": "Learning tool 1"
          },
          "launchEndpoint": {
            "type": "string",
            "format": "uri",
            "example": "https://mytool.ltiaas.com/lti/launch"
          },
          "loginEndpoint": {
            "type": "string",
            "format": "uri",
            "example": "https://mytool.ltiaas.com/lti/login"
          },
          "deeplinkingEndpoint": {
            "type": "string",
            "format": "uri",
            "description": "Falls back to `launchEndpoint` when none was registered.",
            "example": "https://mytool.ltiaas.com/lti/launch"
          },
          "redirectionUris": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            },
            "example": [
              "https://mytool.ltiaas.com/lti/launch"
            ]
          },
          "authConfig": {
            "$ref": "#/components/schemas/AuthConfig"
          },
          "permissions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Permission"
            },
            "example": [
              "MEMBERSHIPS_READ",
              "LINEITEMS_READ"
            ]
          },
          "personalData": {
            "$ref": "#/components/schemas/PersonalData"
          },
          "customParameters": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "example": {}
          },
          "active": {
            "type": "boolean",
            "example": true
          },
          "publicKey": {
            "type": "string",
            "description": "The public half of the key pair LTIAAS uses to sign this tool's ID Tokens. Also published in your [keyset](/connect/api/lti-keys).",
            "example": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n"
          }
        }
      },
      "ServiceRequestBody": {
        "type": "object",
        "required": [
          "payload"
        ],
        "description": "Every service request has the same body: a single signed JWT. Verify it with your consumer public key, then read its `type` and `parameters` claims.",
        "properties": {
          "payload": {
            "type": "string",
            "description": "An RS256 JWT signed by LTIAAS, valid for ten minutes.",
            "example": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
          }
        }
      },
      "ServiceContext": {
        "type": "object",
        "required": [
          "id"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "2022CSEa5e6c431b91"
          },
          "label": {
            "type": "string",
            "example": "CS101"
          },
          "title": {
            "type": "string",
            "example": "Computer Science 101"
          }
        }
      },
      "LineItem": {
        "type": "object",
        "required": [
          "id",
          "label",
          "scoreMaximum"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Your stable identifier for this grade line.",
            "example": "412"
          },
          "label": {
            "type": "string",
            "example": "My first grade line"
          },
          "scoreMaximum": {
            "type": "number",
            "example": 99
          },
          "resourceLinkId": {
            "type": "string",
            "description": "Present when the grade line belongs to a specific activity.",
            "example": "412"
          },
          "tag": {
            "type": "string",
            "description": "Present when the grade line carries a tool-defined tag.",
            "example": "new_tag"
          }
        }
      },
      "LineItemInput": {
        "type": "object",
        "required": [
          "label",
          "scoreMaximum"
        ],
        "properties": {
          "label": {
            "type": "string",
            "example": "My first grade line"
          },
          "scoreMaximum": {
            "type": "number",
            "example": 99
          },
          "resourceLinkId": {
            "type": "string",
            "example": "412"
          },
          "tag": {
            "type": "string",
            "example": "new_tag"
          }
        }
      },
      "Score": {
        "type": "object",
        "required": [
          "userId",
          "activityProgress",
          "gradingProgress"
        ],
        "properties": {
          "userId": {
            "type": "string",
            "example": "41"
          },
          "activityProgress": {
            "type": "string",
            "enum": [
              "Initialized",
              "Started",
              "InProgress",
              "Submitted",
              "Completed"
            ],
            "description": "How far the user has got with the activity.",
            "example": "Completed"
          },
          "gradingProgress": {
            "type": "string",
            "enum": [
              "FullyGraded",
              "Pending",
              "PendingManual",
              "Failed",
              "NotReady"
            ],
            "description": "How far the tool has got with grading it.",
            "example": "FullyGraded"
          },
          "comment": {
            "type": "string",
            "example": "Great work!"
          },
          "scoreGiven": {
            "type": "number",
            "description": "Optional — a score may report progress without a grade.",
            "example": 99
          },
          "scoreMaximum": {
            "type": "number",
            "description": "Required whenever `scoreGiven` is present.",
            "example": 100
          }
        }
      },
      "Result": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Your identifier for the result. Combining the line item and user IDs is fine.",
            "example": "11"
          },
          "userId": {
            "type": "string",
            "example": "41"
          },
          "comment": {
            "type": "string",
            "example": "Great work!"
          },
          "resultScore": {
            "type": "number",
            "example": 99
          },
          "resultMaximum": {
            "type": "number",
            "example": 100
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "example": 403
          },
          "error": {
            "type": "string",
            "example": "Forbidden"
          },
          "details": {
            "type": "object",
            "description": "Context for the failure. Always carries a `message` code; the other keys depend on what went wrong.",
            "properties": {
              "message": {
                "type": "string",
                "example": "INACTIVE_SERVICE"
              },
              "description": {
                "type": "string"
              }
            },
            "additionalProperties": true
          }
        }
      },
      "ValidationError": {
        "type": "object",
        "properties": {
          "status": {
            "type": "integer",
            "example": 400
          },
          "error": {
            "type": "string",
            "example": "Bad Request"
          },
          "details": {
            "type": "object",
            "properties": {
              "errors": {
                "type": "array",
                "description": "One entry per field that failed validation.",
                "items": {
                  "type": "object",
                  "properties": {
                    "field": {
                      "type": "string",
                      "example": "clientId"
                    },
                    "message": {
                      "type": "string",
                      "example": "Missing 'clientId' parameter."
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "responses": {
      "Error": {
        "description": "The request could not be completed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ValidationError": {
        "description": "One or more request parameters were missing or malformed.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ValidationError"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "The `Authorization` header was missing, malformed, or carried the wrong key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "status": 401,
              "error": "Unauthorized",
              "details": {
                "message": "INVALID_BEARER_AUTHORIZATION_HEADER",
                "description": "Invalid API Key for account."
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The account is not permitted to use this endpoint — most often because it is an LTIAAS Launch account rather than a Connect one, or because a trial quota has been reached.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "status": 403,
              "error": "Forbidden",
              "details": {
                "message": "INVALID_ACCOUNT_TYPE"
              }
            }
          }
        }
      },
      "InactiveService": {
        "description": "The LTI® service this endpoint depends on is not enabled on your account.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "status": 403,
              "error": "Forbidden",
              "details": {
                "message": "INACTIVE_SERVICE",
                "service": "Deep Linking"
              }
            }
          }
        }
      },
      "UnregisteredTool": {
        "description": "No tool with that ID is registered on this account.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "status": 404,
              "error": "Not Found",
              "details": {
                "message": "UNREGISTERED_TOOL"
              }
            }
          }
        }
      }
    }
  }
}
