Set the stable base URL and credentials
Obtain a short-lived tenant token from your Platform3 administrator, then use the approved Content test_spec UUID. Keep the complete /bank/integrator_api/implementation/api path.
The Bank API turns one Platform3 test_spec into verified QTI forms or an adaptive pool with native IDs and an immutable proof receipt. Fixed-form generation is owner-certified and independently releasable. Adaptive admission remains closed with typed 424 dependency_contract_unavailable. The legacy route is a separate read-only proof: it reconciles the governed 17-form collection without generating, registering, or assigning a bank.
https://alphatest-andymontgomery-9773s-projects.vercel.app/bank/integrator_api/implementation/api. Certified fixed-form requests may return 202 Accepted after dependency preflight. A request-specific 424 dependency_contract_unavailable creates no operation or upstream mutation. Adaptive and public legacy admission remain fail-closed until their own receipts are published.Prerequisites: a Platform3 Content test_spec ID and a signed tenant JWT whose mapped scopes include bank:write and bank:read. The API reads tenant identity from the verified token—never from the request body. Run this flow from your authenticated backend; a successful create returns an operation to poll, not a bank that is ready immediately.
Obtain a short-lived tenant token from your Platform3 administrator, then use the approved Content test_spec UUID. Keep the complete /bank/integrator_api/implementation/api path.
export ALPHATEST_BANK_URL="https://alphatest-andymontgomery-9773s-projects.vercel.app/bank/integrator_api/implementation/api"
export ALPHATEST_TOKEN="<tenant-jwt>"
export SPEC_ID="<content-test-spec-uuid>"List one operation before wiring writes. A valid token receives 200 and a tenant-scoped collection; an empty collection is a valid first response.
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $ALPHATEST_TOKEN" \
"$ALPHATEST_BANK_URL/v1/bank-operations?limit=1" | jqThe idempotency key makes retrying the exact request safe. The certified success branch returns 202 with an operation ID. A dependency-specific 424 is a closed preflight branch: save its request and trace IDs, do not poll, and retry only after the named dependency is healthy.
HTTP_STATUS="$(curl --silent --show-error \
-X POST "$ALPHATEST_BANK_URL/v1/bank-generations" \
-H "Authorization: Bearer $ALPHATEST_TOKEN" \
-H "Idempotency-Key: acmetest-g5-math-forms-001" \
-H "Content-Type: application/json" \
--data "{\"specId\":\"$SPEC_ID\",\"bankMode\":\"same_blueprint_fixed_forms\",\"formCount\":5}" \
--output /tmp/alphatest-bank-response.json \
--write-out '%{http_code}')"
case "$HTTP_STATUS" in
202) export OPERATION_ID="$(jq -er '.operationId' /tmp/alphatest-bank-response.json)" ;;
424) jq -e '.code == "dependency_contract_unavailable" and (.operationId == null)' /tmp/alphatest-bank-response.json >/dev/null && jq '{code,dependency,capability,sourceUrl,probeResult,requestId,traceId}' /tmp/alphatest-bank-response.json >&2; exit 1 ;;
*) cat /tmp/alphatest-bank-response.json >&2; exit 1 ;;
esacRespect Retry-After. Only status=ready with assignable=true may enter your assignment flow; every other terminal state must stop it.
while :; do
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $ALPHATEST_TOKEN" \
--dump-header /tmp/alphatest-bank-poll-headers \
--output /tmp/alphatest-bank-operation.json \
"$ALPHATEST_BANK_URL/v1/bank-operations/$OPERATION_ID"
RESPONSE="$(cat /tmp/alphatest-bank-operation.json)"
STATUS="$(jq -r '.status' <<<"$RESPONSE")"
ASSIGNABLE="$(jq -r '.assignable' <<<"$RESPONSE")"
printf 'bank operation: %s\n' "$STATUS"
case "$STATUS:$ASSIGNABLE" in
ready:true) jq '.result | {bankId, forms, adaptiveSelection}' <<<"$RESPONSE"; break ;;
ready:*|ready_with_gaps:*|failed:*|cancelled:*) jq '{status,assignable,progress,manifestUrl}' <<<"$RESPONSE"; exit 1 ;;
*) RETRY_AFTER="$(awk 'tolower($1)=="retry-after:" {gsub("\\r", "", $2); print $2}' /tmp/alphatest-bank-poll-headers | tail -1)"; sleep "${RETRY_AFTER:-5}" ;;
esac
doneAfter an admitted operation becomes terminal, fetch manifestUrl with the same read token and save its strong ETag. Fixed forms proceed only from ready/assignable=true with the complete owner receipt. Adaptive additionally requires a non-null adaptivePool reconciled to QTI-owned order and Content-owned primary KC tags.
MANIFEST_URL="$(jq -er '.manifestUrl' /tmp/alphatest-bank-operation.json)"
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $ALPHATEST_TOKEN" \
--dump-header /tmp/alphatest-bank-manifest-headers \
--output "$OPERATION_ID-manifest.json" \
"$ALPHATEST_BANK_URL$MANIFEST_URL"
jq '{manifestHash, gapCount:(.gaps|length), adaptivePool}' \
"$OPERATION_ID-manifest.json"For ready_with_gaps, failed, or cancelled, follow the top-level manifestUrl: gaps[] explains unsupported or failed slots and quarantinedArtifacts[] records retained QTI artifact references, the failure phase, and cleanup state. A pre-admission 424 has no operation and therefore no manifest.
A Platform3 Content bankId, ordered form references, QTI package/artifact/version IDs, and an immutable evidence-manifest URL. Pass those native IDs directly to your assignment flow—there is no AlphaTest ID translation layer. Legacy proof deliberately returns no bankId and is never assignable.
202 and typed-424 contract: dependency readiness · Runtime readiness: BANK-ITD-003 · Release proof: BANK-ITD-016
Admission is capability-specific. Fixed generation has the published SAME_BLUEPRINT_FIXED_FORMS-2026-07-21 owner receipt. Adaptive remains closed until a current immutable replay proves its QTI root, ordered members, and Content-owned KC relationships. Legacy is verification-only: conformance must read the governed collection and reconcile all 17 aliases without any owner mutation.
| Request | Current state | Latest wire evidence |
|---|---|---|
| Fixed generation | Open · owner-certified | Conditional 202 after preflight; published certification SAME_BLUEPRINT_FIXED_FORMS-2026-07-21. Always verify the returned operation's own complete receipt. |
| Adaptive generation | Closed pending publication | Public create returns typed 424 with no operation until a ready/assignable/zero-gap replay publishes a stable non-null adaptivePool. |
| 17-form legacy alias proof | Verification-only · public closed | Immutable conformance may reach ready, but must return assignable=false, bankId=null, ownerMutationCount=0, and exactly 17 ordered aliases. |
ready operation proves aliases only; it must remain non-assignable and must never create a Content bank, QTI artifact, package, or membership. AlphaTest will not treat a local bank, copied UUID list, pool, or implementation status bit as owner proof.Exact gates: Incept generation, QTI ingest, Content registration, legacy alias proof · Fields: 424 Problem extension
Fixed-form requests may return 202 and enter the generated-bank lifecycle. Adaptive and public legacy requests remain typed-424 before admission. Legacy immutable conformance uses a distinct owner-read chain and can terminate ready only as a non-assignable, zero-mutation alias proof.
queued / runningPoll the operation URI. It may expose a Retry-After header.
readyGenerated fixed/adaptive: assignable only with a complete receipt. Legacy: always non-assignable.
ready_with_gapsTerminal, evidence available, never assignable. Inspect gaps.
failed / cancelledTerminal and non-assignable. Created upstream refs remain auditable.
Contract: BANK-ITD-002 · Fields: BankOperation
Certified. Choose same_blueprint_fixed_forms and provide formCount; handle conditional 202 or request-specific typed 424. Assign only after ready/assignable=true and a complete fixed receipt.
Contract published, admission closed. Choose qti_adaptive_selection and omit formCount; today branch on typed 424. Adoption opens only after a certified terminal manifest preserves QTI-owned root/member order and reconciles each member to exactly one matching primary Content KC relationship.
Verification-only and public-closed. Submit only the governed collectionKey; Bank reads all 17 owner-ordered QTI identities and Content aliases. A successful immutable replay is ready but always assignable=false and performs zero owner mutations.
Every route requires a signed, unexpired JWT with sub, role, tenantId, iat, and exp. Role-to-scope mapping must grant bank:read for GETs and bank:write for POSTs. A compatibility X-Timeback-Tenant header, if sent, must exactly match the verified claim.
401 authentication_required; insufficient scope or a mismatched compatibility header returns 403.Decision: BANK-AXIS-006 · Tenant routing: BANK-AXIS-009
/v1/bank-generationsThis route requires bank:write, JSON, and an Idempotency-Key of 1–255 visible ASCII characters. Unknown body properties reject. Certified fixed-form requests may return 202 after preflight. Adaptive requests return 424 dependency_contract_unavailable with no operation until their independent receipt is published.
| Field | Requirement | Meaning |
|---|---|---|
specId | Required UUID | Verbatim Content test_spec ID. |
bankMode | Required enum | same_blueprint_fixed_forms or qti_adaptive_selection. |
formCount | Conditional integer | Fixed only: positive and at least the spec's min_forms; no AlphaTest-specific upper cap is pinned. Forbidden for adaptive. |
Success: 202 Accepted with a BankOperation, Location, strong ETag, and integer-seconds Retry-After. Dependency failure: 424 Problem with dependency, capability, sourceUrl, and probeResult; no operation is created.
Other branches: 404 unknown/cross-tenant spec; 409 key conflict or spec contract violation; 429 backpressure.
curl --silent --show-error \
-X POST "$ALPHATEST_BANK_URL/v1/bank-generations" \
-H "Authorization: Bearer $ALPHATEST_TOKEN" \
-H "Idempotency-Key: acmetest-g5-math-adaptive-001" \
-H "Content-Type: application/json" \
--data "{\"specId\":\"$SPEC_ID\",\"bankMode\":\"qti_adaptive_selection\"}" | \
jq '{status,code,dependency,capability,requestId,traceId,probeResult}'424, code=dependency_contract_unavailable, and no operationId. After adaptive certification, the same request contract may return 202 and enter the documented lifecycle.Decision: dependency readiness, Incept gate, QTI ingest gate, registration gate · Full schema: generation endpoint
/v1/legacy-bank-importsThis route never calls Incept and never accepts member IDs. It loads the owner-published collection, preserves its canonical manifest hash, reads all 17 QTI versions in owner order, and reconciles their Content aliases. Public admission remains typed-424 fail-closed until the legacy receipt is published.
curl --fail-with-body --silent --show-error \
-X POST "$ALPHATEST_BANK_URL/v1/legacy-bank-imports" \
-H "Authorization: Bearer $ALPHATEST_TOKEN" \
-H "Idempotency-Key: timeback-legacy-forms-001" \
-H "Content-Type: application/json" \
--data '{"collectionKey":"timeback-alphatest-legacy-forms-v1"}' | \
jq '{status,code,dependency,capability,requestId,traceId,probeResult}'collectionKey is required and must equal timeback-alphatest-legacy-forms-v1. Additional properties—including qtiArtifactVersionIds, specId, packageId, formId, and formIndex—reject with 400 validation_failed. A caller-held UUID list is not owner proof.
202 and later reach ready, but the terminal result must have assignable=false, bankId=null, ownerMutationCount=0, and exactly 17 aliases[]. It proves identity; it does not create a bank.Decision: legacy alias proof and zero-mutation boundary, credentialed release gate · Full schema: legacy import endpoint
/v1/bank-operationsRequires bank:read. Filters combine with AND. Use opaque cursor paging; never parse a cursor or reuse it with changed filters or sort.
| Query | Allowed shape |
|---|---|
status | One exact OperationStatus. |
kind | generation or legacy_import. |
specId, bankId | Exact Content UUID. |
sort | -createdAt (default), createdAt, -updatedAt, or updatedAt. |
limit | 1–100; default 25. |
cursor | Opaque, 1–2048 characters. |
modifiedSince | Inclusive RFC 3339 updatedAt bound with UTC offset. |
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $ALPHATEST_TOKEN" \
"$ALPHATEST_BANK_URL/v1/bank-operations?status=ready&sort=-updatedAt&limit=25"Returns {"items":[],"nextCursor":null,"hasMore":false} for an empty page; items may contain full BankOperation objects.
Decision: read shape, query model · Full schema: list endpoint
/v1/bank-operations/{operationId}Requires bank:read. Operation IDs match ^bop_[0-9A-HJKMNP-TV-Z]{26}$. Returns a full BankOperation, a strong ETag, and Retry-After while non-terminal. Send If-None-Match to receive a bodyless 304 when unchanged. Unknown and other-tenant IDs both return the same 404.
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $ALPHATEST_TOKEN" \
"$ALPHATEST_BANK_URL/v1/bank-operations/$OPERATION_ID"{
"operationId": "bop_01JAZ2Y8X5K9Q3M7N4T6V8W0RP",
"status": "ready",
"assignable": true,
"result": {
"bankId": "1f628318-603f-4a92-b62e-6e4f204a320d",
"membershipMode": "same_blueprint_fixed_forms",
"forms": [{
"formIndex": 1,
"formId": "5308e1f1-77db-4ce8-bd80-376f951fe0f5",
"packageId": "d6f79061-c548-46ed-a9f3-b8c338745164",
"artifactId": "c31f607a-38ca-42da-8bc5-02ca79ea7770",
"artifactVersionId": "4f4c18f4-c2ec-4278-97e2-2b07a3070d91",
"qtiIdentifier": "alphatest-g5-math-form-a"
}],
"membershipVerifiedAt": "2026-07-15T14:02:09.225Z"
},
"manifestUrl": "/v1/bank-operations/bop_01JAZ2Y8X5K9Q3M7N4T6V8W0RP/manifest"
}Illustrative IDs matching the certified fixed-form result contract; this fixture is not a claim about a particular live operation. The data dictionary contains the complete approved fixed, adaptive, and legacy shapes.
Decision: conditional reads, operation lifecycle · Full schema: detail endpoint
/v1/bank-operations/{operationId}/manifestRequires bank:read and a terminal operation. Non-terminal requests return 409 manifest_not_ready. Success returns an EvidenceManifest attachment with Cache-Control: private, immutable and a strong ETag equal to the quoted manifest hash.
curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer $ALPHATEST_TOKEN" \
-o "$OPERATION_ID-manifest.json" \
"$ALPHATEST_BANK_URL/v1/bank-operations/$OPERATION_ID/manifest"When request.bankMode=qti_adaptive_selection, the manifest requires a non-null adaptivePool. It identifies the tenant-owned QTI root with packageId, artifactId, immutable artifactVersionId, qtiIdentifier, and packageHash. Its non-empty members[] stays in authoritative QTI package order; Bank reconciles every member to a Content item and exactly one matching primary KC relationship.
adaptivePool.Decision: one immutable manifest subresource · Adaptive receipt: BANK-ITD-017 · Every nested key: manifest schema
By contract, every non-2xx response uses application/problem+json with stable type, title, status, safe detail, code, requestId, traceId, and optional field/upstream error arrays. A create-route 424 also carries the typed dependency-preflight fields. Log request and trace IDs; do not branch on human-readable detail.
| Status | Stable codes |
|---|---|
| 400 | validation_failed |
| 401 | authentication_required |
| 403 | insufficient_scope, tenant_mismatch |
| 404 | resource_not_found |
| 409 | idempotency_conflict, spec_contract_violation, manifest_not_ready |
| 415 / 424 / 429 | unsupported_media_type, dependency_contract_unavailable, rate_limited |
| 500 | internal_error |
{
"type": "https://api.alphatest.app/problems/bank/dependency_contract_unavailable",
"title": "Dependency contract unavailable",
"detail": "Adaptive bank generation cannot be admitted until tenant-aligned QTI authority and adaptive-pool conformance are certified.",
"status": 424,
"code": "dependency_contract_unavailable",
"requestId": "req_01JZBANK000000000000000001",
"traceId": "trace_01JZBANK00000000000001",
"dependency": "qti",
"capability": "register_adaptive_pool",
"sourceUrl": "https://platform3-andymontgomery-9773s-projects.vercel.app/qti/1edtech/customer_website",
"probeResult": {"status":403,"code":"forbidden","checkedAt":"2026-07-20T15:13:34.339Z"}
}Retry GETs after transient 429/5xx responses. Retry POSTs only with the same idempotency key and byte-equivalent semantic request. For the current 424, use dependency and capability to identify the exact uncertified seam; do not retry until that capability is certified. Honor Retry-After. See the endpoint-by-endpoint error matrix.
When an admitted operation reaches a terminal state, the manifest binds the request/spec version, converter version, source and QTI hashes, CASE resolutions, slot verdicts, misconception-tagged distractor checks, Platform3 score probes, owner read-back, gaps, timings, and safe upstream trace IDs. Adaptive manifests add the QTI-owned pool and Content-owned KC reconciliation; legacy manifests add legacyProof with the governed collection, 17 aliases, and zero-mutation assertion. A manifest contains hashes and references—not prompts, item bodies, answer keys, learners, responses, attempts, or mastery records.
Standard, DOK, and requested interaction match.
Every wrong selected response retains a distinct Incept-authored misconception tag.
Deterministic QTI 3.0 package is ingested and read back.
Expected response scores correctly in Platform3.
Generated membership—or legacy zero-mutation aliases—reads back exactly.
Manifest definition: EvidenceManifest · Adaptive fields: adaptivePool · Legacy fields: legacyProof · Architecture: distractor verification, adaptive receipt, audit and conformance evidence
If Incept cannot generate a requested interaction, the converter cannot represent it, Platform3 cannot score it, or a selected-response distractor lacks a distinct named misconception, AlphaTest records the exact slot and boundary, links an upstream Issue when available, and names the proof metric for closure. It never swaps in an easier item, invents a misconception tag, or authors an item locally. Reports-owned weak-item revision orders trigger a new Incept generation and full verification chain; only a verified immutable replacement rotates into Content membership, with the old version retained for audit and in-flight administrations. Read the append-only rotation decision.
| Boundary | What the manifest tells you | Assignment outcome |
|---|---|---|
incept | Generation capability or invalid response gap. | ready_with_gaps is always assignable=false; otherwise the operation fails closed. |
converter | JSON semantics cannot be represented faithfully in QTI 3.0. | |
platform3_qti | Ingest or runtime-score proof failed. | |
platform3_case | Required CFItem could not be resolved. | |
platform3_content | Spec or bank membership contract failed. |
modifiedSince is the eventing contract.The approved implementation contract requires a server-only datastore behind the authenticated API. Service-role credentials must never reach clients, every local query must begin with the verified tenantId, and missing and cross-tenant identifiers must be indistinguishable. Upstream credentials must be least-privilege server credentials.
Security model: BANK-ITD-015 · Persistence fields: bank_operation
The approved owner receipt opens fixed-form generation only. Adaptive and legacy remain independent proof branches:
SAME_BLUEPRINT_FIXED_FORMS-2026-07-21 publishes the owner-backed fixed receipt. Each returned operation still must be ready/assignable=true with a complete manifest before assignment.424. Certification requires a ready/assignable/zero-gap immutable replay whose manifest has a stable non-null adaptivePool, QTI-owned member order, and Content-owned primary KC reconciliation.424. Immutable conformance loads timeback-alphatest-legacy-forms-v1 and must prove exactly 17 aliases with bankId=null, ownerMutationCount=0, and assignable=false. It never registers membership.Fail-closed rule still applies. Any request whose own dependency preflight fails returns typed 424 with no operation. Any admitted operation that fails verification stays non-assignable and exposes its manifest. No fixture, copied standard, local scorer, local item, or local Content-membership substitute may produce ready.
Conformance decisions: BANK-AXIS-010 · BANK-ITD-016 release gate · Contract examples: required and conditional shapes
/v1Additive optional response fields and new terminal gap codes are compatible; clients must ignore unknown response fields and tolerate unknown response enum values. Removal, renaming, changed meaning, or narrowing requires a new major path. Unknown request fields and enum values reject.
Compatibility and enum contract: enumerations and compatibility rules