DEVELOPER DOCUMENTATION
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.
Authorization and discovery
Section titled “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.
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
Section titled “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
Section titled “Submit a report”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
Section titled “Triage safely”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
Section titled “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
Section titled “Validate a file-first bundle”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{ "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
Section titled “Preview and publish exact revisions”Mint a preview grant with one closed source mode:
{ "source": "draft", "expectedDraftVersion": 4 }or:
{ "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:
{ "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
Section titled “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 for the exact schemas served by the current deployment.