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

# Assessments API

> Read, update, and delete assessments over the API, invite candidates in bulk, and export candidate results as CSV.

# Assessments

Assessments are the core building block of Gradient. Each assessment defines a task, time limits, available data sources, and a scoring rubric.

## List Assessments

<CodeGroup>
  ```bash cURL theme={null}
  curl https://app.trygradient.ai/api/assessments \
    -H "Authorization: Bearer gai_your_key"
  ```

  ```javascript Node.js theme={null}
  const res = await fetch('https://app.trygradient.ai/api/assessments', {
    headers: { Authorization: `Bearer ${apiKey}` }
  });
  const { assessments, total } = await res.json();
  ```
</CodeGroup>

```
GET /api/assessments
```

Returns all assessments in your organization.

<ParamField query="limit" type="integer" default="20">
  Results per page (1-100)
</ParamField>

<ParamField query="offset" type="integer" default="0">
  Offset for pagination
</ParamField>

<ParamField query="isActive" type="boolean">
  Filter by active status
</ParamField>

<Accordion title="Response">
  ```json theme={null}
  {
    "assessments": [
      {
        "id": "uuid",
        "orgId": "uuid",
        "name": "Senior PM Assessment",
        "role": "Senior Product Manager",
        "taskPrompt": "Create a product strategy deck...",
        "deliverableType": "pptx",
        "taskTimeMinutes": 45,
        "bonusPhaseEnabled": false,
        "bonusPhaseMinutes": 0,
        "connectorConfig": [],
        "seededData": [],
        "availableSkills": [],
        "scoringRubric": { "..." : "..." },
        "candidatePermissions": {
          "can_add_skills": true,
          "can_edit_agents_md": true,
          "can_add_memory": true
        },
        "sendFeedback": false,
        "feedbackDelayHours": 24,
        "dueDate": null,
        "isActive": true,
        "createdAt": "2025-01-15T10:00:00Z",
        "updatedAt": "2025-01-15T10:00:00Z"
      }
    ],
    "total": 5,
    "limit": 20,
    "offset": 0
  }
  ```
</Accordion>

***

## Get Assessment

```
GET /api/assessments/:id
```

Returns a single assessment by ID, plus the org's branding (for candidate-facing display) and whether session-replay capture is enabled for the org.

<Accordion title="Response">
  ```json theme={null}
  {
    "assessment": {
      "id": "uuid",
      "name": "Senior PM Assessment",
      "role": "Senior Product Manager",
      "taskPrompt": "Create a product strategy deck...",
      "deliverableType": "pptx",
      "..."
    },
    "orgBranding": {
      "logoUrl": "https://.../logo.png",
      "wordmarkUrl": null,
      "orgName": "Acme Inc"
    },
    "captureReplaysEnabled": false
  }
  ```

  `orgBranding` is `null` when the org row can't be loaded.
</Accordion>

***

## Update Assessment

```
PATCH /api/assessments/:id
```

Send only the fields you want to update.

**Settings fields:** `name`, `role`, `roleId`, `is_active`, `jd_link`, `due_date`, `send_feedback`, `feedback_delay_hours`, `timer_mode`, `pause_between_phases`. Any org member or API key can change these.

**Content fields:** `task_prompt`, `phases`, `scoring_rubric`, `connector_config`, `candidate_permissions`, `admin_guide`, `task_outline`, `setup_time_minutes`, `task_time_minutes`, `bonus_phase_enabled`, `bonus_phase_minutes`. Only Gradient staff can change these. If a request from another caller includes a content field, the API returns `403` and changes nothing. To change the content of an assessment, contact [hi@trygradient.ai](mailto:hi@trygradient.ai).

### Role

`roleId` is the id of an existing `roles` row to link (Plan 54's Settings-tab role picker). When provided, the server resolves the role's `title` and writes it to the denormalized `role` string, then reconciles the `assessment_roles` join table (unlinking whatever was previously linked, linking the new pick) — mirroring the sequence assessment creation already uses (insert with `role.title`, then a separate link call). `role` remains independently settable as a raw string for callers that don't use the picker; sending both `role` and `roleId` in the same request lets `roleId`'s resolved title win. An unknown or soft-deleted `roleId` returns a `not_found` error rather than silently no-op-ing.

<Note>`send_feedback` and `feedback_delay_hours` are still accepted for backward compatibility but are inert — automated candidate feedback is not currently delivered.</Note>

### Candidate Permissions

The `candidate_permissions` field is a JSON object controlling what candidates can do during the assessment:

| Key | Type | Default | Description |
| - | - | - | - |
| `can_add_skills` | boolean | `true` | Candidates can add/edit/toggle skills |
| `can_edit_agents_md` | boolean | `true` | Candidates can edit the AI system prompt |
| `can_add_memory` | boolean | `true` | Candidates can add persistent memory entries |

<Note>
  `can_browse_connector_documents` is deprecated. Candidates always see connector data files in the workspace **Files** cabinet, so the key has no effect and the admin UI no longer shows it. The API still accepts and returns it, and stored values are unchanged.
</Note>

<Note>
  `can_use_scratchpad` is off by default and no longer a toggle in the admin UI. It is also forced off for every **v7** session (v7 scores the deliverable only), so `GET /api/assessments/:id` returns it as `false` for a v7 org even if a stored value says `true`. The stored value itself is not changed.
</Note>

<Accordion title="Response">
  ```json theme={null}
  {
    "assessment": { "..." }
  }
  ```
</Accordion>

***

## Reviewer Documents

```
GET /api/assessments/:id/reviewer-docs
```

Returns one phase's reviewer document as a downloadable **.docx**, for the Assessment Data tab's **Task Briefs** and **Answer Keys & Exemplars** sections. Both derive from the same source the Reviewer Guide renders, so the shared file never drifts from the in-app guide.

Reviewer/admin only, scoped to the caller's org. A candidate session token is rejected with `403` — the answer-key document would expose the grading key.

<ParamField query="kind" type="string" required>
  `brief` returns the candidate-facing task brief; `key` returns the answer key (what strong sessions do, watchouts, the "if they get it wrong" notes, and the sample strong session).
</ParamField>

<ParamField query="phase" type="string" required>
  The phase id, as it appears in the assessment's `phases` array (or `reflection`). Numbering follows the Reviewer Guide, so the ungraded Explore warm-up is excluded.
</ParamField>

The response is a binary `.docx` with `Content-Disposition: attachment`; the filename leads with the assessment name and phase. A `phase`/`kind` pair with no content (for example a phase whose answer key has not been generated) returns `404`.

***

## Delete Assessment

```
DELETE /api/assessments/:id
```

Soft-deletes the assessment (sets the `deleted_at` timestamp). This is the everyday delete, and it is reversible with Restore Assessment. The assessment no longer appears in the admin lists, but all sessions, scores, connector data, and files stay intact. In-flight sessions and scoring keep working, because by-id reads do not filter soft-deleted assessments. Requires owner or admin. To erase the data permanently, use Purge Assessment.

<Accordion title="Response">
  ```json theme={null}
  {
    "ok": true
  }
  ```
</Accordion>

***

## Restore Assessment

```
POST /api/assessments/:id/restore
```

Restores a soft-deleted assessment from the trash back to the admin lists (clears `deleted_at`). Requires owner or admin, the same as delete.

<Accordion title="Response">
  ```json theme={null}
  {
    "assessment": {
      "id": "...",
      "name": "..."
    }
  }
  ```
</Accordion>

***

## Purge Assessment

```
POST /api/assessments/:id/purge
```

Permanently erases the assessment, all its sessions, scores, events, connector data, and context documents, plus every backing Storage object (rrweb recordings, deliverable files, and data-room files). This action is irreversible.

Only owners and admins can purge (capability `assessments.purge`). API-key callers have no role and are refused: a machine credential cannot trigger an irreversible erasure.

<Warning>This action is irreversible. All candidate data for this assessment will be permanently deleted.</Warning>

<Accordion title="Response">
  ```json theme={null}
  {
    "ok": true
  }
  ```
</Accordion>

***

## Invite Candidate

```
POST /api/assessments/:id/invite
```

Creates a candidate (or finds an existing one by email) and generates an assessment session with a unique invite link. Sends an invitation email automatically unless `sendEmail` is `false`.

<ParamField body="email" type="string" required>
  Candidate's email address
</ParamField>

<ParamField body="name" type="string" required>
  Candidate's display name
</ParamField>

<ParamField body="dueDate" type="string">
  ISO 8601 per-candidate deadline (overrides assessment-level deadline)
</ParamField>

<ParamField body="sendEmail" type="boolean" default="true">
  When `false`, Gradient does not send its default invitation email — the caller is expected to deliver a custom email using the returned `inviteUrl`. Only affects newly-created sessions; idempotent hits on an existing session never re-send (use `POST /api/assessments/:id/invite/resend` instead).
</ParamField>

<Accordion title="Response - 201 Created (new session)">
  ```json theme={null}
  {
    "session": {
      "id": "uuid",
      "token": "abc123"
    },
    "inviteUrl": "/assess/abc123"
  }
  ```
</Accordion>

If the candidate already has an active session for this assessment, returns `200 OK`:

<Accordion title="Response - 200 OK (existing session)">
  ```json theme={null}
  {
    "session": { "id": "uuid", "token": "abc123" },
    "message": "Candidate already has an active session",
    "inviteUrl": "/assess/abc123"
  }
  ```
</Accordion>

***

## Batch Invite Candidates

```
POST /api/assessments/:id/invite/batch
```

Bulk version of `POST /api/assessments/:id/invite`: creates/re-uses a candidate and session for each `{name, email}` pair in one request, instead of one HTTP round trip per candidate. Each row is processed independently — one row failing (e.g. a bad email) doesn't stop the rest — and the response reports a per-row outcome in the same order as the request.

<ParamField body="candidates" type="array" required>
  Array of `{ name: string, email: string, dueDate?: string }`. Max 500 per request; larger pastes should be split client-side.
</ParamField>

<ParamField body="sendEmail" type="boolean" default="false">
  Same semantics as the single-invite endpoint, applied to every row.
</ParamField>

<Accordion title="Response - 200 OK">
  ```json theme={null}
  {
    "results": [
      { "name": "Jane Smith", "email": "jane@example.com", "status": "created", "session": { "id": "uuid", "token": "abc123" }, "inviteUrl": "/assess/abc123" },
      { "name": "Alex Rivera", "email": "alex@example.com", "status": "existing", "session": { "id": "uuid", "token": "def456" }, "inviteUrl": "/assess/def456" },
      { "name": "Bad Row", "email": "not-an-email", "status": "error", "message": "Invalid email address" }
    ]
  }
  ```
</Accordion>

***

## Resend Invite Email

```
POST /api/assessments/:id/invite/resend
```

Resends the invitation email for an existing session.

<ParamField body="sessionId" type="string" required>
  The session ID to resend the invite for
</ParamField>

<Accordion title="Response">
  ```json theme={null}
  {
    "success": true
  }
  ```
</Accordion>

***

## Export Invited Candidates (CSV)

```
GET /api/assessments/:id/sessions/export
```

Downloads the assessment's invited candidates as a CSV file (`Content-Type: text/csv`, `Content-Disposition: attachment`). Every row is one session; every row has every column, blank where a column doesn't apply to that row. Timestamps render as `YYYY-MM-DD HH:mm` in the org's time zone (Excel/Sheets parse this as a datetime). Org-scoped; admin or API-key auth.

Columns, in order:

* **Fixed**: `name, email, link, status, test_candidate, invited_at, started_at, submitted_at, due_at, timezone, time_taken_minutes, scored_at, score, level, feedback_state, feedback_sent_at, flag_count, flags`. `link` is the candidate's absolute `/assess/:token` URL; `test_candidate` is `true`/`false` (email matches a test/internal pattern, same rule as Billing); `level` is the overall band word that the candidate page shows next to the score (`Strong`, `Proficient`, `Developing`, `Limited`, or `Not attempted`), blank when the session has no score; `feedback_state` is `not_scored`, `not_sent`, or `sent`. `flag_count` is the number of integrity flags on the score (blank when the session has no score). `flags` has one line per flag, in the form `<label>: <detail>`, with the same label and sentence that the candidate page shows. The audit `evidence` string is not included.
* **Human review** (only when the org requires human review, or a session in the export has a recorded decision): `review_status, review_note, reviewed_at, reviewed_by`, after `flags`. `review_status` is `Needs review`, `Moving on`, or `Not moving on`, and is blank for a session that is not submitted. `reviewed_by` is the reviewer's name, and is blank for an API key or a removed member.
* **Category levels** (blank on an unscored session): `work_quality_level, ai_fluency_level`. Each is the level word that the candidate page shows for that category (`Advanced`, `Proficient`, `Developing`, `Emerging`, or `Not Shown`), and includes reviewer overrides.
* **v7 numeric sub-scores** (blank on a v5/v6 or unscored session): `work_quality_score, setup_score, direction_score, judgment_score, mindset_score` — each `0-100`.
* **v7 report content** (blank on a v5/v6 or unscored session): for each of `setup, direction, judgment, mindset` — `<axis>_level` (the band word, e.g. `Advanced`), `<axis>_what_happened`, `<axis>_next_time`; then for each scored output, in phase order — `output_<n>_name, output_<n>_level, output_<n>_what_happened, output_<n>_next_time` (`n` runs `1..N`, where `N` is the most outputs any session in this export has scored — a session with fewer leaves the rest blank); then `feedback_verdict`.
* **Priority Skills evidence**: one `priority_<skill>_evidence` column per distinct Priority Skill name on this assessment's rubric, alphabetical (`<skill>` is the name, lowercased with non-alphanumerics collapsed to `_`). Each cell is that skill's trap outcomes and cited examples, one per line — blank when the skill wasn't scored evidence-only or the session predates it.

<Accordion title="Response - 200 OK (text/csv)">
  ```
  name,email,link,status,test_candidate,invited_at,started_at,submitted_at,due_at,timezone,time_taken_minutes,scored_at,score,level,feedback_state,feedback_sent_at,flag_count,flags,work_quality_level,ai_fluency_level,work_quality_score,setup_score,direction_score,judgment_score,mindset_score,setup_level,setup_what_happened,setup_next_time,direction_level,direction_what_happened,direction_next_time,judgment_level,judgment_what_happened,judgment_next_time,mindset_level,mindset_what_happened,mindset_next_time,output_1_name,output_1_level,output_1_what_happened,output_1_next_time,feedback_verdict,priority_stakeholder_mapping_evidence
  Jane Smith,jane@example.com,https://app.trygradient.ai/assess/abc123,invited,false,2026-07-15 13:00,,,,America/New_York,,,,,not_scored,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  "Sousa, Alex",alex@example.com,https://app.trygradient.ai/assess/def456,scored,false,2026-07-15 13:00,2026-07-15 13:05,2026-07-15 14:10,,America/New_York,65,2026-07-15 14:20,84.0,Proficient,not_sent,,0,,Proficient,Proficient,84,80,88,85,70,Proficient,Scoped the task well.,Write the plan down first.,Advanced,Redirected once and caught the miss.,Redirect sooner next time.,Advanced,Caught a bad assumption before it shipped.,Keep it up.,Developing,Reflected on one tradeoff.,Go a level deeper next time.,Proposal,Proficient,Clear structure with a stated tradeoff.,Add a risk section.,Strong hire,"Trap caught: ""Checked with Finance before sending."" Caught the missing stakeholder."
  ```
</Accordion>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.