> ## Documentation Index
> Fetch the complete documentation index at: https://docs.examino.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Exams API - List, Create, Read, and Update Exams

> Create and manage exams, configure grading settings, and control archiving via the Exams API. Requires an API key with the appropriate scope.

The Exams API is the starting point for everything in Examino. An exam holds your subject, rubric, and all student copies. You create an exam here, configure its settings with `PATCH`, and then use the Copies and Corrections APIs to import student work and launch grading. All endpoints require a Bearer API key passed in the `Authorization` header.

***

## List exams

<ParamField query="teamId" type="string">
  Restrict results to a specific team. Defaults to the entire subtree accessible by your API key.
</ParamField>

<ParamField query="limit" type="integer" default={50}>
  Maximum number of exams to return per page. Cannot exceed **200**.
</ParamField>

<ParamField query="offset" type="integer" default={0}>
  Number of exams to skip before returning results. Use with `limit` to paginate.
</ParamField>

<ParamField query="archived" type="string">
  Pass `true` to include archived exams in results. Archived exams are excluded by default.
</ParamField>

<Note>
  Results are sorted newest first (`createdAt` descending).
</Note>

<Warning>
  The `total` field in the response reflects the count of items **on the returned page**, not the size of the full collection. Paginate by incrementing `offset` until a page returns fewer items than your `limit`.
</Warning>

**Required scope:** `exams:read`

```bash title="Request" theme={null}
curl "https://app.examino.ai/api/v1/exams?teamId=d4e5f6a7-b8c9-40d1-a2b3-c4d5e6f7a8b9&limit=50" \
  -H "Authorization: Bearer $EXAMINO_API_KEY"
```

```json title="Response 200" theme={null}
{
  "items": [
    {
      "id": "aa11bb22-cc33-44dd-88ee-ff0011223344",
      "teamId": "d4e5f6a7-b8c9-40d1-a2b3-c4d5e6f7a8b9",
      "title": "Marketing digital, partiel S1",
      "description": null,
      "level": "Bachelor 2",
      "lang": "fr",
      "gradingMode": "notes",
      "toneOfVoice": "formal",
      "severity": 2,
      "archived": false,
      "questionsValidated": true,
      "createdAt": "2026-09-02T07:44:19.310Z",
      "updatedAt": "2026-09-14T16:20:51.004Z"
    }
  ],
  "total": 1
}
```

***

## Create an exam

Creates a new exam as a team-visible draft with an empty subject and neutral organization defaults. The exam is owned by the target workspace and attributed to the API key, it is not attached to the user who created that key and does not inherit that user's preferences. After creation, use `PATCH /exams/{examId}` to set the title and configure grading options, then import the subject and validate the rubric in the web app before launching corrections.

**Required scope:** `exams:write`

<ParamField body="teamId" type="string">
  The team that will own this exam. Defaults to the root team of your API key.
</ParamField>

<Warning>
  A `folder`-type team cannot hold exams. You must target a `workspace`-type team, or the request will be rejected.
</Warning>

```bash title="Request" theme={null}
curl -X POST https://app.examino.ai/api/v1/exams \
  -H "Authorization: Bearer $EXAMINO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"teamId": "d4e5f6a7-b8c9-40d1-a2b3-c4d5e6f7a8b9"}'
```

```json title="Response 201" theme={null}
{
  "id": "aa11bb22-cc33-44dd-88ee-ff0011223344",
  "teamId": "d4e5f6a7-b8c9-40d1-a2b3-c4d5e6f7a8b9"
}
```

<Tip>
  After creation, call `PATCH /exams/{examId}` to set the title, language, and grading severity before importing any student copies. Organization members see **Created by API** in the web app; administrators can inspect the exact key name and public prefix.
</Tip>

***

## Get an exam

Returns the full detail object for a single exam, including validation flags that gate correction launches.

**Required scope:** `exams:read`

```bash title="Request" theme={null}
curl "https://app.examino.ai/api/v1/exams/aa11bb22-cc33-44dd-88ee-ff0011223344" \
  -H "Authorization: Bearer $EXAMINO_API_KEY"
```

```json title="Response 200" theme={null}
{
  "id": "aa11bb22-cc33-44dd-88ee-ff0011223344",
  "teamId": "d4e5f6a7-b8c9-40d1-a2b3-c4d5e6f7a8b9",
  "title": "Marketing digital, partiel S1",
  "description": null,
  "level": "Bachelor 2",
  "lang": "fr",
  "gradingMode": "notes",
  "toneOfVoice": "formal",
  "severity": 2,
  "archived": false,
  "questionsValidated": true,
  "subjectFilesValidated": true,
  "copyFilesValidated": true,
  "createdAt": "2026-09-02T07:44:19.310Z",
  "updatedAt": "2026-09-14T16:20:51.004Z"
}
```

### Response fields

<ResponseField name="id" type="string">
  Unique UUID for the exam.
</ResponseField>

<ResponseField name="teamId" type="string">
  UUID of the team that owns this exam.
</ResponseField>

<ResponseField name="title" type="string | null">
  Display name of the exam, up to 300 characters.
</ResponseField>

<ResponseField name="description" type="string | null">
  Optional freeform description, up to 2000 characters.
</ResponseField>

<ResponseField name="level" type="string | null">
  Academic level label, e.g. `Bachelor 2`. Up to 200 characters.
</ResponseField>

<ResponseField name="lang" type="string | null">
  Language code for the exam content, e.g. `fr`, `en`, `de`, `es`.
</ResponseField>

<ResponseField name="gradingMode" type="string">
  `notes` for numeric grading or `competencies` for competency-based grading.
</ResponseField>

<ResponseField name="toneOfVoice" type="string">
  Style used for AI-generated feedback: `familiar` (casual, encouraging) or `formal` (professional).
</ResponseField>

<ResponseField name="severity" type="integer">
  Grading strictness level: `1` = lenient, `2` = standard, `3` = strict.
</ResponseField>

<ResponseField name="archived" type="boolean">
  `true` if the exam has been archived and is hidden from default list results.
</ResponseField>

<ResponseField name="questionsValidated" type="boolean">
  `true` when the rubric has been validated. **This must be `true` before you can launch a correction.**
</ResponseField>

<ResponseField name="subjectFilesValidated" type="boolean">
  `true` when the subject document has been validated.
</ResponseField>

<ResponseField name="copyFilesValidated" type="boolean">
  `true` when uploaded copy files have passed processing.
</ResponseField>

***

## Update an exam

Performs a partial update on an exam, only the fields you include in the request body are changed. Sending an empty body returns `400 invalid_request`.

**Required scope:** `exams:write`

<ParamField body="title" type="string | null">
  Display name for the exam. Maximum 300 characters. Pass `null` to clear.
</ParamField>

<ParamField body="description" type="string | null">
  Optional description. Maximum 2000 characters. Pass `null` to clear.
</ParamField>

<ParamField body="level" type="string | null">
  Academic level, e.g. `Bachelor 2`. Maximum 200 characters. Pass `null` to clear.
</ParamField>

<ParamField body="lang" type="string | null">
  Language code for the exam, between 2 and 10 characters (e.g. `fr`, `en`, `de`, `es`). Pass `null` to clear.
</ParamField>

<ParamField body="toneOfVoice" type="string">
  AI feedback style: `familiar` or `formal`.
</ParamField>

<ParamField body="severity" type="integer">
  Grading strictness: `1` (lenient), `2` (standard), or `3` (strict).
</ParamField>

<ParamField body="archived" type="boolean">
  Pass `true` to archive the exam or `false` to unarchive it.
</ParamField>

<Note>
  `gradingMode` is locked once the subject is validated and cannot be changed via the API. Class assignment is also managed exclusively in the web app. Neither field is available in the v1 API.
</Note>

```bash title="Request" theme={null}
curl -X PATCH https://app.examino.ai/api/v1/exams/aa11bb22-cc33-44dd-88ee-ff0011223344 \
  -H "Authorization: Bearer $EXAMINO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title": "Marketing digital, partiel S1", "lang": "fr", "severity": 2}'
```

The response is the full updated exam object, identical in shape to the `GET /exams/{examId}` response.

<AccordionGroup>
  <Accordion title="Field validation rules">
    * `title`: string or null, max 300 chars
    * `description`: string or null, max 2000 chars
    * `level`: string or null, max 200 chars
    * `lang`: string or null, 2–10 chars
    * `toneOfVoice`: must be exactly `"familiar"` or `"formal"`
    * `severity`: must be exactly `1`, `2`, or `3`
    * `archived`: boolean, using `null` returns `400 invalid_request`
    * Unknown fields are ignored and do not cause an error
  </Accordion>

  <Accordion title="What cannot be changed via PATCH">
    The following properties are read-only through the v1 API:

    * **`gradingMode`**, locked once the subject is validated. Change it before importing subject files.
    * **`teamId`**, exams cannot be moved between teams via the API.
    * **Class assignment**, managed in the web app only.
  </Accordion>
</AccordionGroup>
