> ## 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.

# Sessions

> Track assessment sessions and candidate activity

# Sessions

A session represents one candidate's attempt at one assessment. Sessions progress through a defined lifecycle from invitation to review.

**Session statuses:** `invited` → `setup` → `in_progress` → `submitted` → `scoring` → `scored` → `reviewed`

## List Sessions

```
GET /api/sessions
```

<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="assessmentId" type="string">
  Filter by assessment
</ParamField>

<ParamField query="candidateId" type="string">
  Filter by candidate
</ParamField>

<ParamField query="status" type="string">
  Filter by status: `invited`, `setup`, `in_progress`, `submitted`, `scoring`, `scored`, `reviewed`
</ParamField>

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

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

<Accordion title="Response">
  ```json theme={null}
  {
    "sessions": [
      {
        "id": "uuid",
        "assessmentId": "uuid",
        "candidateId": "uuid",
        "token": "abc123",
        "status": "submitted",
        "setupStartedAt": "2025-01-15T10:00:00Z",
        "taskStartedAt": "2025-01-15T10:15:00Z",
        "submittedAt": "2025-01-15T11:00:00Z",
        "dueDate": null,
        "createdAt": "2025-01-15T09:00:00Z",
        "candidateName": "Jane Smith",
        "candidateEmail": "jane@example.com",
        "assessmentName": "Senior PM Assessment"
      }
    ],
    "total": 8,
    "limit": 20,
    "offset": 0
  }
  ```
</Accordion>

***

## Get Session

```
GET /api/sessions/:sessionId
```

Returns a single session with candidate and assessment metadata.

<Accordion title="Response">
  ```json theme={null}
  {
    "session": {
      "id": "uuid",
      "assessmentId": "uuid",
      "candidateId": "uuid",
      "token": "abc123",
      "status": "scored",
      "candidateName": "Jane Smith",
      "candidateEmail": "jane@example.com",
      "assessmentName": "Senior PM Assessment",
      "setupStartedAt": "2025-01-15T10:00:00Z",
      "taskStartedAt": "2025-01-15T10:15:00Z",
      "submittedAt": "2025-01-15T11:00:00Z"
    }
  }
  ```
</Accordion>

***

## Delete Session

```
DELETE /api/sessions/:sessionId
```

Permanently deletes a session and all associated messages, events, and scores.

<Warning>This action is irreversible.</Warning>

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

***

## Send Reminder

```
POST /api/sessions/:sessionId/remind
```

Emails the candidate a due-date reminder for an incomplete session (status `invited`, `setup`, or `in_progress`) and stamps `reminderSentAt`. Uses the session's due date, falling back to the assessment's.

Fails with `400` when the candidate has already submitted or there is no due date to remind about, and with `429` when a reminder was already sent within the last 24 hours.

<Accordion title="Response">
  ```json theme={null}
  {
    "ok": true,
    "reminderSentAt": "2026-07-12T09:30:00Z"
  }
  ```
</Accordion>

***

## Advance Phase

```
POST /api/sessions/:sessionId/advance-phase
```

Advances a multi-phase assessment session to the next phase. Saves the current phase's deliverable state and resets the timer for the next phase. Called by the candidate workspace when the candidate clicks "Next Phase."

<Note>This endpoint is authenticated via the candidate session token, not an API key.</Note>

<ParamField body="deliverableState" type="object">
  The current phase's deliverable content (email state, document state, etc.)
</ParamField>

<ParamField body="timeRemainingAtEnd" type="integer" default="0">
  Seconds remaining on the current phase timer when the candidate advanced
</ParamField>

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

***

## Checkpoint Phase

```
POST /api/sessions/:sessionId/checkpoint
```

Saves the current phase's deliverable state to the database without advancing or submitting. Enables interim saves so candidate work survives browser crashes and tab closes. The checkpoint is debounced client-side (3s) and fires after prompt edits and workflow runs.

<Note>This endpoint is authenticated via the candidate session token, not an API key.</Note>

<ParamField body="deliverableState" type="object" required>
  The current phase's deliverable content (workflow state, email state, etc.)
</ParamField>

<ParamField body="recordSnapshot" type="boolean" default="false">
  When `true`, also persist a point-in-time snapshot of the deliverable for the replay/history timeline, not just the latest interim save.
</ParamField>

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

***

## Get Session Events

```
GET /api/sessions/:sessionId/events
```

Returns the event log for a session - every action the candidate took during the assessment. Useful for audit trails and session replay.

<ParamField query="limit" type="integer" default="100">
  Results per page
</ParamField>

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

<ParamField query="type" type="string">
  Filter by event type (see list below)
</ParamField>

**Event types:** `connector_browsed`, `connector_connected`, `connector_disconnected`, `skill_uploaded`, `skill_created`, `skill_activated` (legacy), `skill_enabled`, `skill_disabled`, `agents_md_edited`, `memory_added`, `user_message`, `ai_response`, `ai_tool_use`, `slash_command`, `manual_edit`, `slide_added`, `slide_deleted`, `slide_reordered`, `section_added`, `section_deleted`, `section_reordered`, `content_edited`, `deliverable_downloaded`, `deliverable_reuploaded`, `file_uploaded`, `task_submitted`, `time_expired`, `security_flag`, `ai_feedback`, `phase_advanced`, `inbox_item_opened`, `inbox_item_closed`, `workflow_run`, `workflow_prompt_edited`, `session_opened`, `session_resumed`, `session_disconnected`, `timer_warning`, `doc_viewed`, `web_search`, `chat_cleared`, `deliverable_text_typed`, `deliverable_paste`

**Integrity event types** (recorded for reviewer context only — never an auto-penalty): `copy_detected`, `paste_detected`, `tab_blur`, `tab_focus`, `idle_gap`, `deliverable_paste`

<Accordion title="Response">
  ```json theme={null}
  {
    "events": [
      {
        "id": "uuid",
        "sessionId": "uuid",
        "timestampMs": 1705312800000,
        "phase": "task",
        "type": "user_message",
        "data": { "content": "..." },
        "createdAt": "2025-01-15T10:00:00Z"
      }
    ],
    "total": 156,
    "limit": 100,
    "offset": 0
  }
  ```
</Accordion>

## Record Session Chunk

```
POST /api/sessions/:sessionId/recording
```

Appends one flushed batch of rrweb DOM-recording events (cursor, clicks, scroll, typing) as an ordered chunk, streamed from the candidate workspace every 5s. Fire-and-forget — a dropped chunk never surfaces to the candidate.

<Note>This endpoint is authenticated via the candidate session token, not an API key.</Note>

<ParamField body="events" type="array" required>
  Batch of rrweb events for this chunk
</ParamField>

<ParamField body="seq" type="integer" required>
  Client-assigned monotonic chunk sequence number (0-based, incremented once per flush)
</ParamField>

<ParamField body="startedTs" type="string" required>
  ISO timestamp of the first event in the batch
</ParamField>

<ParamField body="endedTs" type="string" required>
  ISO timestamp of the last event in the batch
</ParamField>

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

***

## Get Session Recording

```
GET /api/sessions/:sessionId/recording
```

Returns the concatenated rrweb screen recording for a session (cursor, clicks, scroll, typing), used by the staff replay player. Recordings are only present for sessions taken after screen recording shipped; `hasRecording` is `false` otherwise.

<Accordion title="Response">
  ```json theme={null}
  {
    "sessionId": "uuid",
    "hasRecording": true,
    "events": [{ "type": 2, "timestamp": 1705312800000 }],
    "startedMs": 1705312800000,
    "endedMs": 1705313400000,
    "chunkCount": 12
  }
  ```
</Accordion>

***

## Preview Feedback Report

```
GET /api/sessions/:sessionId/feedback-preview
```

Admin-only preview of the candidate feedback report — the same data the candidate sees at their `/assess/:token/feedback` link, rendered on the candidate detail page. Unlike the candidate-facing route, this does not gate on release: it's available as soon as the session is scored, whether or not feedback has actually been released to the candidate. Read-only; never sends anything or changes release state.

<Accordion title="Response">
  ```json theme={null}
  {
    "sessionId": "uuid",
    "hasRecording": true,
    "feedbackText": "...",
    "headline": "...",
    "strengths": [{ "text": "...", "dimensionId": "deliverable" }],
    "weaknesses": [{ "text": "...", "dimensionId": "ai_fluency" }],
    "aiUseSuggestions": [{ "text": "...", "impact": "..." }],
    "assessmentName": "Senior PM Assessment",
    "role": "Senior PM",
    "finalScore": 82,
    "percentileGlobal": 74,
    "axes": [
      {
        "id": "deliverable",
        "name": "Deliverable",
        "score": 3.2,
        "max": 4,
        "subSkills": [
          { "id": "communication", "name": "Communication", "score": 3, "max": 4, "level": "strong", "source": "ai", "why": "Led with a clear recommendation" }
        ]
      }
    ],
    "feedbackReleasedAt": null,
    "release": {
      "releasedAt": null,
      "sendFeedback": false,
      "delayHours": 24,
      "scoredAt": "2025-01-15T10:00:00Z",
      "candidateEmail": "jane@example.com",
      "releaseDueAt": "2025-01-16T10:00:00Z"
    }
  }
  ```

  The `release` object mirrors the eligibility signals the release cron uses, so the Feedback tab can explain why a scored session's feedback hasn't gone out yet.
</Accordion>

***

## Preview Feedback Email

```
GET /api/sessions/:sessionId/email-preview
```

Admin-only preview of the candidate feedback email — builds the exact subject and HTML `sendFeedbackEmail` would send (same template, same score-derived content), but returns it as JSON instead of sending. Never calls the email provider.

<Accordion title="Response">
  ```json theme={null}
  {
    "subject": "Your Gradient feedback report is ready, Jane.",
    "html": "<!DOCTYPE html>...",
    "to": "jane@example.com"
  }
  ```
</Accordion>
