Skip to content
OpenTrain AIOpenTrain AIOpenTrain AIDocs

Ask OpenTrain

Answers from the documentation, with sources.

What would you like to do with OpenTrain?

AI answers can be mistaken. Check the linked sources. Don’t include private account information.

Open app

DEVELOPER DOCUMENTATION

Quiz Lifecycle Events and Webhooks

Nine typed project_todo.quiz.* events for assignment, reminders, submissions, grading, release, pass/fail and To-do completion, pushed by webhooks and served by GET /updates.

GET/api/public/v1/updates

Every change to a quiz To-do produces one typed event. A webhook subscription pushes it to your endpoint, and GET /updates serves the same record. Your agent can follow a quiz from assignment to completion without opening OpenTrain.

Requirements: project_todos:read to see these events. To subscribe a webhook you also need webhooks:manage and the webhooks feature.

typeWhen it happensExtra data fields
project_todo.quiz.assignedA quiz To-do is assigned and bound to its published version. Once per assignment.completionPolicy
project_todo.quiz.reminder_dueAn open quiz To-do needs a reminder: its deadline is close, it is overdue, or a required To-do without a deadline is still unopened. Once per reminder step.reminderKind, dueAt, cadenceBucket
project_todo.quiz.attempt_submittedThe AI trainer submits an attempt. Once per attempt.attemptId, attemptNumber, attemptKind, reviewCycleId, gradeState, resultReleased
project_todo.quiz.grading_requiredA submitted attempt has manually graded questions that wait for your grade. Once per attempt.attemptId, attemptNumber, pendingQuestionCount
project_todo.quiz.gradedA grade is saved. PARTIAL leaves questions ungraded; FINALIZED grades all of them. Once per grade revision.attemptId, attemptNumber, revisionNumber, gradeState, outcome, afterRelease
project_todo.quiz.result_releasedThe result becomes visible to the AI trainer, either automatically or through your release. Once per attempt.attemptId, attemptNumber, releaseMode, releasedAt
project_todo.quiz.passedThe attempt passes. This fires at submit for an automatically graded quiz, or on the grade that first decides the result or changes it.attemptId, attemptNumber, revisionNumber, previousOutcome
project_todo.quiz.failedThe attempt fails. Same timing as passed.attemptId, attemptNumber, revisionNumber, previousOutcome
project_todo.quiz.todo_completedThe quiz To-do is completed: by the submission, by a passing grade, or by your approval.completedVia, attemptId

Every event’s data also carries assignmentId (the same value as the event’s resourceId), bindingId, formId and formVersionId. jobId on the event envelope is the job.

passed and failed only fire for quizzes with a pass rule. A new grade that keeps the same result sends graded but no second passed or failed. revisionNumber is null when the automatic grade at submit decided the result.

FieldValues
completionPolicyON_SUBMIT, ON_REVIEW, ON_PASS
reminderKindDUE_SOON, OVERDUE, UNVIEWED (dueAt is null for UNVIEWED)
attemptKindFULL, or REMEDIATION for a selective coaching revision (reviewCycleId is set)
gradeState on attempt_submittedGRADED, PENDING_MANUAL
gradeState on gradedPARTIAL, FINALIZED
outcome, previousOutcomePASSED, FAILED, or null (not decided, or no pass rule)
releaseModeIMMEDIATE, MANUAL
completedViaSUBMISSION, PASS, EMPLOYER_APPROVAL (attemptId is null for an approval)

Payloads hold IDs and states only. They never contain answers, answer keys, rubrics, points, grading notes, names or email addresses. To read the details of an attempt, call GET /quizzes/attempts/{attemptId} with the attemptId from the event.

OpenTrain writes each event in the same database transaction as the change it describes, keyed on that change. So:

  • A retried or concurrent submit, grade, release or approval produces exactly one event per change.
  • An event is never lost while its change is saved. If the event cannot be stored, the change is rolled back.

Webhook deliveries can still repeat after a network failure. Dedupe on the X-OpenTrain-Delivery header or on the event id.

Terminal window
curl -X POST "https://www.opentrain.ai/api/public/v1/webhooks" \
-H "Authorization: Bearer $OPENTRAIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/opentrain-webhook",
"eventTypes": [
"project_todo.quiz.attempt_submitted",
"project_todo.quiz.grading_required",
"project_todo.quiz.todo_completed"
]
}'
Terminal window
opentrain webhooks create --url https://example.com/opentrain-webhook \
--events project_todo.quiz.attempt_submitted,project_todo.quiz.grading_required,project_todo.quiz.todo_completed --json

In MCP, call the webhook create tool with the same eventTypes. The quiz capabilities document (GET /project-todos/quizzes/authoring/capabilities, opentrain todos quiz capabilities, or the opentrain_quiz_authoring_capabilities tool) lists all nine types under workflows.events.

{
"id": "981245",
"type": "project_todo.quiz.graded",
"apiVersion": "v1",
"createdAt": "2026-11-02T10:15:04.000Z",
"resourceId": "assignment_123",
"jobId": "job_456",
"data": {
"assignmentId": "assignment_123",
"bindingId": "binding_789",
"formId": "form_321",
"formVersionId": "version_654",
"attemptId": "attempt_987",
"attemptNumber": 1,
"revisionNumber": 2,
"gradeState": "FINALIZED",
"outcome": "PASSED",
"afterRelease": false
}
}

Selective coaching has its own project_todo.quiz.coaching.* events. See coaching.