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

# Reviewer Feedback and Task Checklists API

> Employer-only REST endpoints for reviewer reports, reviewer access, adaptive checklist authoring, visual preview, publishing, and submission review.

The Public API exposes Reviewer Feedback at `/api/public/v1/job-feedback` and adaptive Task Checklists at `/api/public/v1/job-checklists`. Both families require a stored OpenTrain API token and canonical employer access to the exact job.

<Warning>
  Reviewer Feedback reads are employer-only. Do not copy report data into a
  worker-visible system. OpenTrain never returns this ledger to the reported
  AI trainer through an agent surface.
</Warning>

## Authorization and discovery

| Family            | Read scope            | Write scope            |
| ----------------- | --------------------- | ---------------------- |
| Reviewer Feedback | `job_feedback:read`   | `job_feedback:write`   |
| Task Checklists   | `job_checklists:read` | `job_checklists:write` |

The write scope implies read. Probe `GET /api/public/v1/capabilities` before using either family; its `agentSurfaces` entry reports the rollout and per-operation authorization state. The feature must also be enabled separately for each job before its tab appears to employers or workers.

All mutations require an `Idempotency-Key` header. Reuse a stable key only for the same intent and payload. The response's `replayed` field reports whether the server returned the original durable outcome.

The TypeScript SDK exposes the same operations in `@opentrain-ai/sdk` 0.13.0
or later. Reviewer Feedback methods include `listJobFeedback`,
`getJobFeedbackReport`, `getJobFeedbackSummary`, `listJobFeedbackRoster`,
`submitJobFeedback`, `triageJobFeedbackReport`,
`linkJobFeedbackSubject`, reviewer-access methods, and settings methods. Task
Checklist methods include list/get/create/manage/validate/resolve/publish,
version and submission reads, reorder, settings, capabilities, and
`previewJobChecklistGrant`.

```ts theme={null}
import { OpenTrainClient } from '@opentrain-ai/sdk';

const opentrain = new OpenTrainClient({
  apiToken: process.env.OPENTRAIN_API_TOKEN!,
});

const feedback = await opentrain.getJobFeedbackSummary(jobId);
const checklists = await opentrain.listJobChecklists(jobId);
```

## Reviewer Feedback endpoints

| Method | Path                                              | Purpose                                                              |
| ------ | ------------------------------------------------- | -------------------------------------------------------------------- |
| `GET`  | `/job-feedback?jobId={jobId}`                     | List a bounded report page with authoritative status counts.         |
| `POST` | `/job-feedback`                                   | Submit one employer-authored report about a canonical subject.       |
| `GET`  | `/job-feedback/summary?jobId={jobId}`             | Read total, unresolved, status, per-person, and unlinked aggregates. |
| `GET`  | `/job-feedback/roster?jobId={jobId}`              | Discover canonical subjects and all contracts held on the job.       |
| `GET`  | `/job-feedback/{reportId}`                        | Read one report and its append-only event history.                   |
| `POST` | `/job-feedback/{reportId}/triage`                 | Change status using the report's exact `updatedAt` value.            |
| `POST` | `/job-feedback/{reportId}/link-subject`           | Reconcile a legacy unlinked report to a canonical subject once.      |
| `GET`  | `/job-feedback/jobs/{jobId}/reviewer-access`      | List eligible active-contract reviewers and their grant state.       |
| `POST` | `/job-feedback/jobs/{jobId}/reviewer-access`      | Grant or revoke one person's feedback-authoring access.              |
| `POST` | `/job-feedback/jobs/{jobId}/reviewer-access/bulk` | Grant or revoke up to 200 people transactionally.                    |
| `GET`  | `/job-feedback/jobs/{jobId}/settings`             | Read the default-off per-job feature state.                          |
| `POST` | `/job-feedback/jobs/{jobId}/settings`             | Enable or disable the job feature with `confirm: true`.              |

The list endpoint accepts `status`, `subject`, `severity`, `reporter`, `search`, `createdFrom`, `createdTo`, and 1-based `page` filters. Use `subject=UNLINKED` for legacy rows without a canonical person. `hasMore` is the only continuation signal; never infer completeness from the returned row count.

### Submit a report

```bash theme={null}
curl -sS https://app.opentrain.ai/api/public/v1/job-feedback \
  -H "Authorization: Bearer $OT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: feedback-task-482-v1" \
  -d '{
    "jobId": "<job-id>",
    "subjectUserId": "<user-id>",
    "externalTrainerId": "masked-482",
    "externalTaskId": "task-482",
    "severity": "MAJOR",
    "suggestedAction": "RETRAINING",
    "report": "The response repeatedly applies the wrong acceptance rule to ambiguous regions."
  }'
```

`subjectContractId` is optional. When omitted, OpenTrain stores a deterministic contract reference: the newest active contract, otherwise the newest ended contract. Use the roster endpoint if the caller needs to choose a specific contract.

### Triage safely

```bash theme={null}
curl -sS https://app.opentrain.ai/api/public/v1/job-feedback/<report-id>/triage \
  -H "Authorization: Bearer $OT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: feedback-actioned-482-v1" \
  -d '{
    "toStatus": "ACTIONED",
    "expectedUpdatedAt": "<updated-at-from-get>",
    "note": "Assigned the focused retraining module and paused production work."
  }'
```

Terminal transitions to `ACTIONED` or `DISMISSED`, and reopening from a terminal state, require a nonblank note. A stale `expectedUpdatedAt` returns `409`; get the report again and deliberately re-evaluate the transition.

## Task Checklist endpoints

| Method  | Path                                                      | Purpose                                                                       |
| ------- | --------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `GET`   | `/job-checklists/capabilities`                            | Read the live schema, limits, operations, and optional job state.             |
| `GET`   | `/job-checklists?jobId={jobId}`                           | List every definition in display order plus the per-job enabled state.        |
| `POST`  | `/job-checklists`                                         | Create one complete draft atomically.                                         |
| `POST`  | `/job-checklists/validate`                                | Validate a complete local bundle without writing.                             |
| `POST`  | `/job-checklists/reorder`                                 | Reorder the complete definition set with a compare-and-set guard.             |
| `GET`   | `/job-checklists/{definitionId}`                          | Read the employer builder detail and draft revision.                          |
| `PATCH` | `/job-checklists/{definitionId}`                          | Update the draft, retire, or restore a definition.                            |
| `POST`  | `/job-checklists/{definitionId}/validate`                 | Validate the stored draft or published tree.                                  |
| `POST`  | `/job-checklists/{definitionId}/resolve`                  | Resolve caller-supplied sample answers without storing them.                  |
| `POST`  | `/job-checklists/{definitionId}/preview-grant`            | Mint a short-lived browser URL bound to an exact draft or published revision. |
| `POST`  | `/job-checklists/{definitionId}/publish`                  | Publish an immutable version with draft and live-version guards.              |
| `GET`   | `/job-checklists/{definitionId}/versions`                 | Page immutable versions newest first.                                         |
| `GET`   | `/job-checklists/{definitionId}/versions/{versionNumber}` | Read one immutable version and tree.                                          |
| `GET`   | `/job-checklists/submissions?jobId={jobId}`               | Filter and page employer-visible submission rows.                             |
| `GET`   | `/job-checklists/submissions/{submissionId}`              | Read one version-pinned submitted tree and active answers.                    |
| `POST`  | `/job-checklists/jobs/{jobId}/settings`                   | Enable the feature, or disable it with explicit confirmation.                 |

### Validate a file-first bundle

```bash theme={null}
curl -sS https://app.opentrain.ai/api/public/v1/job-checklists/validate \
  -H "Authorization: Bearer $OT_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d @checklist-validate.json
```

```json checklist-validate.json theme={null}
{
  "mode": "inline",
  "title": "Image QC",
  "required": true,
  "tree": [
    {
      "id": "domain",
      "type": "SINGLE_CHOICE",
      "text": "What is your domain specialization?",
      "requiredBoolean": true,
      "options": [
        { "id": "automotive", "label": "Automotive" },
        { "id": "general", "label": "General" }
      ]
    },
    {
      "id": "car-count",
      "type": "NUMBER",
      "text": "How many cars are in the image?",
      "requiredBoolean": true,
      "numberConfig": { "integerOnly": true, "min": 0, "max": 20 }
    },
    {
      "id": "cars",
      "type": "REPEAT_GROUP",
      "text": "Inspect each car",
      "repeatConfig": {
        "countNodeId": "car-count",
        "maxInstances": 20,
        "instanceLabelText": "Car"
      },
      "children": [
        {
          "id": "car-edges",
          "type": "CHECKBOX",
          "text": "Verify the object boundary",
          "requiredBoolean": true
        }
      ]
    }
  ]
}
```

Inline validation can include `baselineDefinitionId` and `expectedDraftVersion` to calculate publish impact and detect a stale local base before writing. Use the exact same server validator for create, draft update, and publish readiness—do not reimplement its rules in a client.

### Preview and publish exact revisions

Mint a preview grant with one closed source mode:

```json theme={null}
{ "source": "draft", "expectedDraftVersion": 4 }
```

or:

```json theme={null}
{ "source": "published", "versionNumber": 2 }
```

The response returns a short-lived `previewUrl` whose fragment carries the capability. Open that URL in an authenticated browser. It renders the exact bound snapshot and strips the fragment after exchange.

Publish only after previewing:

```json theme={null}
{
  "expectedDraftVersion": 4,
  "expectedPublishedVersionNumber": 2,
  "confirmPublish": true
}
```

The response reports the immutable version and content hash, the change impact, open runs swept, and answers that workers must reconfirm. Read the canonical definition afterward rather than incrementing a version locally.

## Errors and privacy

| Status | Meaning                                                           | Recovery                                                                      |
| ------ | ----------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `400`  | Invalid enum, tree, transition, filter, confirmation, or limit.   | Correct the structured request; do not retry unchanged.                       |
| `403`  | Missing scope, unavailable family, or no employer organization.   | Inspect capabilities and use the correct claimed employer token.              |
| `404`  | The job or resource is absent or not canonically accessible.      | Resolve access without probing resource existence.                            |
| `409`  | Stale revision, invalid lifecycle state, or idempotency conflict. | Re-read canonical state and deliberately re-evaluate.                         |
| `429`  | Write throttle exceeded.                                          | Honor `retryAfterSeconds`; keep the same idempotency key for the same intent. |

Use the machine-readable [OpenAPI document](https://app.opentrain.ai/api/public/v1/openapi.json) for the exact schemas served by the current deployment.
