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

# Candidates API

> List, update, and remove the candidates in your organization, export them as CSV, and restore or permanently purge a deleted candidate.

# Candidates

Candidates are the people being assessed. A candidate can have multiple sessions across different assessments.

## List Candidates

```
GET /api/candidates
```

<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 to candidates who have sessions on this assessment
</ParamField>

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

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

<Accordion title="Response">
  ```json theme={null}
  {
    "candidates": [
      {
        "id": "uuid",
        "orgId": "uuid",
        "email": "jane@example.com",
        "name": "Jane Smith",
        "createdAt": "2025-01-15T10:00:00Z"
      }
    ],
    "total": 12,
    "limit": 20,
    "offset": 0
  }
  ```
</Accordion>

***

## Export Candidates (CSV)

```
GET /api/candidates/export
```

Downloads the org's candidates as a CSV across every assessment — the org-wide sibling of `GET /api/assessments/:id/sessions/export` (see that endpoint for the full column list and an example row). Adds an `assessment` column as the first column, since rows now span more than one assessment. Org-scoped; admin or API-key auth.

<ParamField query="assessmentId" type="string">
  Narrow the export to one assessment (same shape as omitting it, one fewer click than exporting from that assessment's own page).
</ParamField>

<Accordion title="Response - 200 OK (text/csv)">
  ```
  assessment,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
  AI Strategy Screen,Jane Smith,jane@example.com,https://app.trygradient.ai/assess/abc123,invited,false,2026-07-15 13:00,,,,America/New_York,,,,,not_scored,,,,,,,,,,,,,,,,,,,,,,,,,,,,
  ```
</Accordion>

***

## Update Candidate

```
PATCH /api/candidates/:id
```

<ParamField body="name" type="string">
  Updated name
</ParamField>

<ParamField body="email" type="string">
  Updated email address
</ParamField>

<Accordion title="Response">
  ```json theme={null}
  {
    "candidate": {
      "id": "uuid",
      "email": "jane@example.com",
      "name": "Jane Smith",
      "createdAt": "2025-01-15T10:00:00Z"
    }
  }
  ```
</Accordion>

***

## Delete Candidate

```
DELETE /api/candidates/:id
```

Soft-deletes the candidate (sets the `deleted_at` timestamp). This is the everyday "Remove". The candidate no longer appears in list results, but the row, the candidate PII, and all session history stay intact. The action is reversible with Restore Candidate. It is not an erasure. To erase the data permanently, use Purge Candidate.

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

***

## Bulk Delete Candidates

```
POST /api/candidates/bulk/remove
```

Soft-deletes every listed candidate in one request instead of one request per candidate, with the same behavior as Delete Candidate above. This route is dispatch-and-collect: it always returns `200`, even when some items fail, and each item carries its own outcome. The single-candidate route above still exists for the per-row action.

<ParamField body="candidateIds" type="string[]" required>
  Up to 500 candidate ids. Duplicates are removed before processing.
</ParamField>

<Accordion title="Response">
  ```json theme={null}
  {
    "results": [
      { "id": "candidate-1", "ok": true },
      { "id": "candidate-2", "ok": false, "error": "Candidate not found" }
    ]
  }
  ```
</Accordion>

***

## Restore Candidate

```
POST /api/candidates/:id/restore
```

Restores a soft-deleted candidate from the trash back to the active lists (clears `deleted_at`). This is open to any org member. It fails if the candidate was permanently purged, because a purged row keeps no data to restore.

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

## Purge Candidate

```
POST /api/candidates/:id/purge
```

Permanently erases the candidate and every session artifact (transcripts, scores, events, rrweb recordings, and deliverable files, plus the backing Storage objects). It then scrubs the candidate PII and keeps a PII-free tombstone row. This action is irreversible.

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

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


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