> ## Documentation Index
> Fetch the complete documentation index at: https://doc.quippy-lab.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Endpoint reference

> Partner API v1 endpoints, parameters, and responses.

Base URL `https://api-sg.quippy-lab.com`. All endpoints require an
`Authorization: Bearer qk_…` header ([Authentication](/partner-api/authentication))
and are scoped to your institution.

## List tests

```
GET /api/partner/v1/tests
```

Query parameters:

| Param    | Default | Notes |
| -------- | ------- | ----- |
| `limit`  | 20      | 1–100 |
| `offset` | 0       | ≥ 0   |

Returns the tests published to your institution.

```json theme={null}
{
  "data": [
    {
      "testId": "exm_01...",
      "title": "English Entry — Sample A",
      "description": "Entry assessment, four skills",
      "durationMinutes": 60,
      "proctoring": "none",
      "marking": "manual",
      "resultVisibility": "after_review"
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0
}
```

| Field              | Values                          | Meaning                       |
| ------------------ | ------------------------------- | ----------------------------- |
| `proctoring`       | `none` \| `record` \| `live_ai` | How the test is supervised    |
| `marking`          | `auto` \| `manual` \| `hybrid`  | How it's scored               |
| `resultVisibility` | `immediate` \| `after_review`   | When the student sees results |

## Create an assignment

```
POST /api/partner/v1/assignments
```

Optional header: `Idempotency-Key: <unique string>` (retry-safe for 24 h).

Request body:

```json theme={null}
{
  "externalUserId": "your-sub-123",
  "testId": "exm_01...",
  "user": { "email": "student@example.com", "name": "Jordan Lee" },
  "callbackUrl": "https://portal.example.com/return",
  "dueAt": "2026-08-01T00:00:00Z",
  "metadata": { "cohort": "spring-2026" }
}
```

Responds `201` with the assignment plus a signed `startUrl` and
`startUrlExpiresAt`. See [Assign a test](/partner-api/assign-a-test#2-create-an-assignment)
for the field table.

## Get an assignment

```
GET /api/partner/v1/assignments/{assignmentId}
```

Status-polling fallback — [webhooks](/webhooks/events) are the primary signal.

```json theme={null}
{
  "assignmentId": "pas_01...",
  "status": "started",
  "externalUserId": "your-sub-123",
  "testId": "exm_01...",
  "startUrl": "https://procto.quippy-lab.com/verify?examId=exm_01...#token=...",
  "startUrlExpiresAt": "2026-07-30T00:00:00Z",
  "result": { "available": false, "score": null },
  "metadata": { "cohort": "spring-2026" },
  "createdAt": "2026-07-23T08:30:00Z",
  "startedAt": "2026-07-23T09:00:00Z",
  "submittedAt": null,
  "completedAt": null
}
```

A fresh `startUrl` is included while the assignment is still startable
(`assigned` or `started`). Once available, `result.score` mirrors the score
object from the `exam.completed` / `grading.done` webhooks.

## Assignment status

```
assigned → started → submitted → completed
                       └→ marking → completed
```

| Status      | Meaning                                                |
| ----------- | ------------------------------------------------------ |
| `assigned`  | Created; the student hasn't started                    |
| `started`   | The student began the test                             |
| `submitted` | Submitted; awaiting marking (transient)                |
| `marking`   | Manual / AI marking in progress (`after_review` tests) |
| `completed` | Finished; `result` is available                        |
| `expired`   | Not started before `dueAt`                             |

## Errors

All errors share the envelope `{ "error": { code, message, requestId } }` —
see the [error table](/partner-api/authentication#errors).
