Bring
A Platform JWT, Content contentTestSpecId, a OneRoster target, and either an authoritatively published fixed-form Content bank as qtiTestId or the adaptive execution pins and exact Results learner bindings.
Assign a Platform3 test to a OneRoster class, authorize each learner’s QTI session, rotate a parallel form on retake, and close into verified Results references—without authoring items, copying rosters, or computing scores.
AlphaTest is the orchestration seam between an existing Platform3 test, OneRoster target, QTI player, and Results. It stores administration policy and correlation glue only.
A Platform JWT, Content contentTestSpecId, a OneRoster target, and either an authoritatively published fixed-form Content bank as qtiTestId or the adaptive execution pins and exact Results learner bindings.
An administration ID, window-authorized deliverySessionRef values, and verified resultRecordRef values.
Identities stay in OneRoster, delivery in QTI, adaptive state in mastery_engine, and durable outcomes and mastery in Results.
Base URL: https://alphatest-administration.vercel.app/api. Paths below are API-root-relative, so /v1/administrations expands beneath that base. Documentation is public; every API operation requires a verified bearer JWT. wire convention · authentication fields · ITD-027
deliverySessionRef only inside [opensAt, closesAt). QTI does not enforce timeLimitSeconds or terminate a session already revealed, and v1 rejects every non-default accommodation. window contract · launch receipt · ITD-031424 dependency_contract_unavailable before admission, until owner-backed certification clears the six-part predicate. After that gate opens, Administration create still verifies the same-tenant Content bank is published, marked as a mastery gate, uses same_blueprint_fixed_forms, matches contentTestSpecId, has the exact non-empty ordered member set, and that every immutable QTI member version dereferences. A reachable mismatch is pre-effect 422 test-not-assignable; an unavailable owner is retryable 424. current Bank posture · six-part predicate · ITD-037| Response | Meaning | What AcmeTest does |
|---|---|---|
Bank 424 dependency_contract_unavailable | No Bank operation was admitted. | Keep the generation request, surface the upstream gap, and do not poll or invent IDs. |
Administration 422 test-not-assignable | Content/QTI were reachable, but the first ordered assignability predicate failed before effects. | Fix the native IDs or owner state; do not retry the unchanged request. |
Administration 424 content-unavailable or qti-unavailable | The predicate could not be evaluated; this does not prove it false. | After recovery, retry the identical request with the same Idempotency-Key. |
Administration 202 plus required Location | The administration and retry-safe provisioning intent are durable. | Follow Location; never synthesize the administration ID or infer completion. |
normative client branching · create contract · ITD-014 · ITD-037
These Bash commands are copy-paste runnable with curl and jq. Set a window that brackets the run, and use a class whose QTI attempts you can submit in your existing player.
set -euo pipefail
: "${ALPHATEST_JWT:?Set a Platform JWT with role integrator or reviewer}"
: "${QTI_TEST_ID:?Set the published Content fixed-form bank id accepted by QTI}"
: "${CONTENT_TEST_SPEC_ID:?Set its Content test_spec id}"
: "${ONEROSTER_CLASS_ID:?Set an active OneRoster class sourcedId}"
: "${OPENS_AT:?Set an RFC 3339 UTC instant before this run}"
: "${CLOSES_AT:?Set an RFC 3339 UTC instant after this run}"
BASE_URL="https://alphatest-administration.vercel.app/api"
AUTH="Authorization: Bearer ${ALPHATEST_JWT}"
JSON="Content-Type: application/json"Expected: 202 Accepted, a body with id, and a Location header after the fixed-gate predicate passes. A reachable bank/spec/member mismatch returns pre-effect 422 test-not-assignable; its safe diagnostic detail names the first failed predicate in the documented six-step order. Do not retry unchanged IDs. Reuse the key only when retrying this logical create.
CREATE_KEY="acmetest-${ONEROSTER_CLASS_ID}-fall-gate-v1"
jq -n --arg qti "$QTI_TEST_ID" --arg spec "$CONTENT_TEST_SPEC_ID" \
--arg class "$ONEROSTER_CLASS_ID" --arg opens "$OPENS_AT" --arg closes "$CLOSES_AT" '{
test:{qtiTestId:$qti,contentTestSpecId:$spec,testKind:"mastery_gate"},
target:{type:"class",oneRosterSourcedId:$class},
window:{opensAt:$opens,closesAt:$closes},
timing:{timeLimitSeconds:5400},
retakes:{maxAttempts:3,parallelFormRotation:"different_form_required"},
accommodations:{default:{timeMultiplier:1},candidateOverrides:[]}
}' >/tmp/alphatest-create.json
curl --fail-with-body --silent --show-error \
--dump-header /tmp/alphatest-create.headers \
--output /tmp/alphatest-create.out \
--request POST "${BASE_URL}/v1/administrations" \
--header "$AUTH" --header "$JSON" \
--header "Idempotency-Key: ${CREATE_KEY}" \
--data-binary @/tmp/alphatest-create.json
test "$(awk 'NR==1{print $2}' /tmp/alphatest-create.headers)" = "202"
ADMIN_ID="$(jq -er '.id' /tmp/alphatest-create.out)"
printf 'Administration: %s\n' "$ADMIN_ID"create endpoint · request fields · 422 diagnostic order · idempotency · ITD-014 · ITD-037
The candidates collection intentionally omits raw session references. Call the per-candidate launch action inside the window to receive a deliverySessionRef, then pass it unchanged to your QTI player. If bounded upstream fan-out partially fails, resume only failed candidates.
RESUME_COUNT=0
while :; do
ADMIN="$(curl --fail-with-body --silent --show-error --header "$AUTH" \
"${BASE_URL}/v1/administrations/${ADMIN_ID}")"
STATUS="$(jq -r '.status' <<<"$ADMIN")"
case "$STATUS" in
scheduled|open) break ;;
provisioning_with_errors)
((RESUME_COUNT+=1)); [[ "$RESUME_COUNT" -le 5 ]] || { jq . <<<"$ADMIN"; exit 1; }
curl --fail-with-body --silent --show-error \
--request POST "${BASE_URL}/v1/administrations/${ADMIN_ID}/provision" \
--header "$AUTH" --header "$JSON" \
--header "Idempotency-Key: provision-${ADMIN_ID}-${RESUME_COUNT}" \
--data '{}' >/tmp/alphatest-provision.out
sleep 2 ;;
provisioning) sleep 2 ;;
*) printf 'Unexpected status: %s\n' "$STATUS" >&2; exit 1 ;;
esac
done
: >/tmp/alphatest-launches.ndjson
CURSOR=""
while :; do
CANDIDATE_URL="${BASE_URL}/v1/administrations/${ADMIN_ID}/candidates?limit=100"
if [[ -n "$CURSOR" ]]; then
CANDIDATE_URL="${CANDIDATE_URL}&cursor=$(jq -rn --arg value "$CURSOR" '$value|@uri')"
fi
PAGE="$(curl --fail-with-body --silent --show-error --header "$AUTH" "$CANDIDATE_URL")"
while IFS= read -r CANDIDATE_ID; do
CANDIDATE_PATH="$(jq -rn --arg value "$CANDIDATE_ID" '$value|@uri')"
LAUNCH_KEY="launch-${ADMIN_ID}-${CANDIDATE_ID}-1"
curl --fail-with-body --silent --show-error \
--request POST \
--header "$AUTH" --header "Idempotency-Key: ${LAUNCH_KEY}" \
"${BASE_URL}/v1/administrations/${ADMIN_ID}/candidates/${CANDIDATE_PATH}/launch" \
| tee -a /tmp/alphatest-launches.ndjson | jq -er \
'{oneRosterSourcedId,deliverySessionRef,timeLimitSeconds,runtimeEnforced}'
done < <(jq -r '.data[].oneRosterSourcedId' <<<"$PAGE")
[[ "$(jq -r '.hasMore' <<<"$PAGE")" == "true" ]] || break
CURSOR="$(jq -er '.nextCursor' <<<"$PAGE")"
doneExpected launch status is 200. A pre-window call returns 403 launch-not-open; at or after close it returns 410 launch-window-ended, even for a stored idempotent replay.
candidate paging · launch endpoint · replay precedence · ITD-031
Submit each launched attempt through your existing QTI player first. Close returns 409 close-not-ready while any launched candidate lacks a submitted latest attempt or that attempt is still in progress; once every launched candidate has submitted, it returns 202.
Your client sends only the strict {} body, its Platform JWT, and the idempotency key. Server-side, AlphaTest reads the QTI score, writes one Results record, and exact-reads by qtiAttemptId; it exposes scored only after the QTI, Content, administration, test-kind, subject, and Results correlations agree exactly. Platform3 service credentials never enter this request or its response.
CLOSE_KEY="close-${ADMIN_ID}-v1"
curl --fail-with-body --silent --show-error \
--dump-header /tmp/alphatest-close.headers \
--output /tmp/alphatest-close.out \
--request POST "${BASE_URL}/v1/administrations/${ADMIN_ID}/close" \
--header "$AUTH" --header "$JSON" \
--header "Idempotency-Key: ${CLOSE_KEY}" --data '{}'
test "$(awk 'NR==1{print $2}' /tmp/alphatest-close.headers)" = "202"
test "$(jq -r '.status' /tmp/alphatest-close.out)" = "closing"
while :; do
ADMIN="$(curl --fail-with-body --silent --show-error --header "$AUTH" \
"${BASE_URL}/v1/administrations/${ADMIN_ID}")"
case "$(jq -r '.status' <<<"$ADMIN")" in
scored) break ;;
close_failed) jq . <<<"$ADMIN"; exit 1 ;;
closing) sleep 2 ;;
*) jq . <<<"$ADMIN"; exit 1 ;;
esac
done
: >/tmp/alphatest-results.ndjson
CURSOR=""
while :; do
RESULTS_URL="${BASE_URL}/v1/administrations/${ADMIN_ID}/candidates?limit=100"
if [[ -n "$CURSOR" ]]; then
RESULTS_URL="${RESULTS_URL}&cursor=$(jq -rn --arg value "$CURSOR" '$value|@uri')"
fi
PAGE="$(curl --fail-with-body --silent --show-error --header "$AUTH" "$RESULTS_URL")"
jq -ce '.data[] | select(.state == "scored" and has("resultRecordRef"))' \
<<<"$PAGE" >>/tmp/alphatest-results.ndjson
test "$(jq '.data | length' <<<"$PAGE")" = \
"$(jq '[.data[] | select(.state == "scored" and has("resultRecordRef"))] | length' <<<"$PAGE")"
[[ "$(jq -r '.hasMore' <<<"$PAGE")" == "true" ]] || break
CURSOR="$(jq -er '.nextCursor' <<<"$PAGE")"
done
test -s /tmp/alphatest-results.ndjson
jq -r '[.oneRosterSourcedId,.resultRecordRef] | @tsv' /tmp/alphatest-results.ndjsonclose endpoint · close semantics · resultRecordRef · ITD-030
202, an administration id, and Location; exact replay returns the original response with Idempotent-Replayed: true.sourcedId, but deliverySessionRef appears only after a successful in-window launch authorization.qtiFormId.scored only when verifiedResults = submittedAttempts and failed = 0; every scored candidate has a resultRecordRef backed by exactly one settled Results record matching the QTI attempt and Content context.retryable:true, honors Retry-After, and preserves operation keys across uncertain outcomes.Use the same test.testKind selector and administration resource for every kind. Fixed forms bind an existing qtiTestId; adaptive diagnostics omit it and supply immutable mastery-engine, Bank, Content, and Results bindings.
{"test":{"qtiTestId":"qti_gate_5_math","contentTestSpecId":"test_spec_gate_5_math","testKind":"mastery_gate"},"target":{"type":"class","oneRosterSourcedId":"class_5a"},"window":{"opensAt":"2026-08-20T13:00:00Z","closesAt":"2026-08-22T21:00:00Z"},"timing":{"timeLimitSeconds":5400},"retakes":{"maxAttempts":3,"parallelFormRotation":"different_form_required"},"accommodations":{"default":{"timeMultiplier":1},"candidateOverrides":[]}}The supplied fixed bank must pass the authoritative Content/QTI predicate before create has any effect. After a submitted/scored attempt, retake selection excludes used forms and returns 409 parallel-forms-exhausted when the declared attempt limit or unused inventory is exhausted. Close returns the binary gate evidence through the Results-owned record, not a locally computed score.
{"test":{"contentTestSpecId":"test_spec_math_placement","testKind":"adaptive_diagnostic"},"target":{"type":"candidate_list","oneRosterCandidateSourcedIds":["user_017"]},"adaptiveConfig":{"selectionPolicyId":"policy_math_v4","scaleCalibrationId":"cal_math_2026","testBankId":"test_bank_math_adaptive","bankOperationId":"bankop_01J...","runMode":"production_learner","resultsStudentBindings":[{"candidateSourcedId":"user_017","resultsStudentId":"student_result_017"}]},"window":{"opensAt":"2026-08-20T13:00:00Z","closesAt":"2026-08-29T21:00:00Z"},"retakes":{"maxAttempts":2,"parallelFormRotation":"not_applicable"},"accommodations":{"default":{"timeMultiplier":1},"candidateOverrides":[]}}Create returns 202 and provisions one mastery-engine run per candidate. Launch reveals its current native QTI offer. After the player scores that offer, call POST …/candidates/user_017/advance with {} and a new idempotency key; the response atomically exposes the next offer or progresses toward submitted/scored. An expired continuation requires a policy-permitted retake, which starts a fresh run.
adaptive fields · advance endpoint · four-state mapping · ITD-032 · ITD-036
{"test":{"qtiTestId":"qti_quiz_fractions_1","contentTestSpecId":"test_spec_fractions_1","testKind":"formative"},"target":{"type":"class","oneRosterSourcedId":"class_5a"},"window":{"opensAt":"2026-08-20T13:00:00Z","closesAt":"2026-08-20T21:00:00Z"},"timing":{"timeLimitSeconds":1200},"retakes":{"maxAttempts":1,"parallelFormRotation":"not_required"},"accommodations":{"default":{"timeMultiplier":1},"candidateOverrides":[]}}Use the identical create → launch → close flow. QTI scores immediately; Results owns per-standard facts and this API returns only the verified resultRecordRef.
Call only while the administration is open and the candidate’s latest QTI state is submitted or scored. A new attempt gets a new key; a network retry of that same attempt reuses it.
: "${ADMIN_ID:?Run the quickstart first}"
: "${CANDIDATE_SOURCED_ID:?Set the submitted candidate sourcedId}"
RETAKE_KEY="retake-${ADMIN_ID}-${CANDIDATE_SOURCED_ID}-attempt-2"
curl --fail-with-body --silent --show-error \
--request POST "${BASE_URL}/v1/administrations/${ADMIN_ID}/retakes" \
--header "$AUTH" --header "$JSON" \
--header "Idempotency-Key: ${RETAKE_KEY}" \
--data "$(jq -n --arg id "$CANDIDATE_SOURCED_ID" '{oneRosterSourcedId:$id}')" \
| tee /tmp/alphatest-retake.json \
| jq -er '{oneRosterSourcedId,qtiFormId,attemptNumber,state}'
test "$(jq -r '.attemptNumber' /tmp/alphatest-retake.json)" = "2"Expected status is 202. Ineligible state is 409 administration-state-conflict; exhausted policy or eligible inventory is 409 parallel-forms-exhausted. endpoint · body and guards · ITD-017
Every operation verifies an HS256 JWT with sub, role, tenantId, iat, and exp. The integrator and reviewer roles grant read, write, sensitive-read, deliver, and close permissions; optional scope, school, and class claims can only narrow them. X-Timeback-Tenant is an assertion and never selects a tenant.
claims · permissions · ITD-007 · ITD-010
{
"type":"https://alphatest-administration.vercel.app/problems/qti-unavailable",
"title":"QTI runtime unavailable",
"status":424,
"detail":"The required QTI operation could not complete.",
"instance":"/v1/administrations/adm_123#occurrence_456",
"code":"qti-unavailable",
"requestId":"req_01J...",
"retryable":true,
"errors":[],
"dependency":"qti"
}
| Condition | Client action | Contract |
|---|---|---|
retryable:false | Do not retry unchanged; fix auth, input, policy, state, or inventory. | ProblemDetails |
retryable:true | Back off exponentially with jitter and honor Retry-After. | header · ITD-024 |
| Uncertain POST outcome | Reuse the same idempotency key and identical semantic request. | idempotency · ITD-006 |
close_failed | Retry close with the original key; reconciliation resumes only unverified attempts. | close recovery |
412 etag-mismatch | GET current policy, review it, then intentionally reapply with the new strong ETag. | If-Match · ITD-005 |
All ten shipped operations live below /v1. Effectful operations use JSON except launch, which has no body. Every row links to the normative field dictionary and governing architecture decision.
/v1/administrationsValidate owner-backed test assignability, then create and asynchronously provision a fixed-form or adaptive administration. Fixed mastery-gate mismatch returns 422 before effects and names the first failed predicate; owner unavailability returns 424.
matrix · request · fixed-gate predicate · 422 detail · ITD-032 · ITD-037
/v1/administrations/{id}/provisionResume only failed candidate provisioning with the original deterministic child keys; successful QTI sessions and mastery runs are untouched.
matrix · strict {} body · progress · ITD-034
/v1/administrationsList tenant administrations by status, test kind, QTI test, target, or modification cursor.
/v1/administrations/{id}Read orchestration state and strong ETag; optionally compose up to 100 candidates.
/v1/administrations/{id}Replace future AlphaTest-owned policy sections before any bound candidate leaves not_started.
matrix · body · concurrency · ITD-005
/v1/administrations/{id}/candidatesPage OneRoster candidate references composed with current fixed-QTI or adaptive state and verified Results references; raw sessions are absent.
/v1/administrations/{id}/candidates/{candidateSourcedId}/launchAuthorize launch against the current window and reveal the opaque fixed session or current adaptive native-QTI offer. No request body or Content-Type.
/v1/administrations/{id}/candidates/{candidateSourcedId}/advanceAfter QTI scores the current adaptive offer, advance the bound mastery run and atomically replace its opaque continuation, ETag, expiry, and next offer.
/v1/administrations/{id}/retakesCreate the next attempt: rotate to a different mastery-gate form, or start a fresh adaptive mastery run and continuation.
/v1/administrations/{id}/closeStop launches and retakes. Fixed forms read QTI outcomes and verify exact Results write-back; adaptive runs must already be owner-verified scored with acknowledged Results KC components.
matrix · body and reconciliation · progress · ITD-030 · ITD-036
| Field | Owner | Integrator use | Normative definition |
|---|---|---|---|
qtiTestId | Content / QTI | For fixed kinds, pass the native Content bank reference unchanged; AlphaTest validates its relationship and every immutable QTI member version. Omit it for adaptive. | TestRef.qtiTestId · assignability |
contentTestSpecId | Content | Pass the blueprint-of-record test_spec reference unchanged for every kind. | TestRef.contentTestSpecId |
testBankId / bankOperationId | Content / Bank | Pin the terminal native adaptive pool and its verified Bank operation. | adaptiveConfig |
oneRosterSourcedId | OneRoster | Address a class or candidate; never substitute name or email. | TargetInput |
resultsStudentId | Results | Bind each adaptive OneRoster candidate to an existing same-tenant Results learner; never infer it. | ResultsStudentBinding |
deliverySessionRef | QTI | Hand unchanged to the player, only from a successful launch receipt. | LaunchReceipt |
qtiFormId | QTI | Verify a mastery-gate retake selected a different fixed form. | CandidateDelivery |
adaptiveRunRef | mastery_engine | Use for support and inspect correlation; continuation and ETag remain server-only. | CandidateDelivery |
resultRecordRef | Results | Dereference the verified durable result; never compute or store a local score. | CandidateDelivery |
timeLimitSeconds remains nominal and non-default accommodations are rejected. Reopen only after authenticated QTI create/update probes prove native duration and accommodation semantics.modifiedSince. Reopen when QTI and mastery_engine expose a common monotonic runtime cursor.dictionary gap ledger · ITD-002 · ITD-008 · ITD-012 · ITD-018 · ITD-035 · ITD-037
This website is the customer-facing specification generated from the approved 24 July 2026 architecture and data dictionary bundled at this origin. Those source pages retain their approved content and stable anchors. This documentation cell certifies contract consistency; it does not claim that the later implementation, security, upstream-wire, latency, or deployed-example tests have passed.
Active and superseded ITDs, upstream ownership, lifecycle, security, and every decision axis.
Every claim, header, field, query, request, response, error, endpoint, and persisted column.
Not claimed here Deployed HTTP, anonymous/cross-tenant, QTI/Results wire, and worked-example proof belong to implementation and QC.