AAlphaTestBank API
Bank orchestration for AcmeTest

Wire the Bank API. Know exactly when a bank is assignable.

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.

Incept generatesAlphaTest orchestrates + verifiesPlatform3 stores + scores
Quickstart · certified fixed-form flow

Token to assignable bank

Stable base URL: 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.

1

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.

Shell · set once
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>"
2

Prove the token and read scope

List one operation before wiring writes. A valid token receives 200 and a tenant-scoped collection; an empty collection is a valid first response.

Shell · authenticated list
curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer $ALPHATEST_TOKEN" \
  "$ALPHATEST_BANK_URL/v1/bank-operations?limit=1" | jq
3

Request five parallel forms

The 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.

Shell
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 ;;
esac
4

Poll until the fixed bank is assignable

Respect Retry-After. Only status=ready with assignable=true may enter your assignment flow; every other terminal state must stop it.

Shell
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
done
5

Download the immutable verification manifest

After 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.

Shell · terminal evidence
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.

What a certified fixed ready operation gives you

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

Certification status · approved 2026-07-24

Fixed forms are certified; adaptive and public legacy admission are closed

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.

RequestCurrent stateLatest wire evidence
Fixed generationOpen · owner-certifiedConditional 202 after preflight; published certification SAME_BLUEPRINT_FIXED_FORMS-2026-07-21. Always verify the returned operation's own complete receipt.
Adaptive generationClosed pending publicationPublic create returns typed 424 with no operation until a ready/assignable/zero-gap replay publishes a stable non-null adaptivePool.
17-form legacy alias proofVerification-only · public closedImmutable conformance may reach ready, but must return assignable=false, bankId=null, ownerMutationCount=0, and exactly 17 ordered aliases.
Do not generalize one mode's receipt. Fixed certification does not open adaptive or legacy. A legacy 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

Operation lifecycle

One observable operation

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.

ResolveContent spec + CASE
GenerateIncept JSON items
ConvertDeterministic QTI 3.0
ScorePlatform3 runtime
RegisterContent + Administration admission
queued / running

Poll the operation URI. It may expose a Retry-After header.

ready

Generated fixed/adaptive: assignable only with a complete receipt. Legacy: always non-assignable.

ready_with_gaps

Terminal, evidence available, never assignable. Inspect gaps.

failed / cancelled

Terminal and non-assignable. Created upstream refs remain auditable.

Contract: BANK-ITD-002 · Fields: BankOperation

Top three jobs

Use the shape that matches your bank

01

Generate parallel gate forms

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.

Wire the generation request →
02

Generate an adaptive pool

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.

Handle the closed request →
03

Prove the 17 legacy aliases

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.

See the exact request shape →
Authentication

Bearer JWT, tenant-scoped by default

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.

Do not send service credentials to a browser. Call AlphaTest from your authenticated backend. Missing, invalid, or expired tokens return 401 authentication_required; insufficient scope or a mismatched compatibility header returns 403.

Decision: BANK-AXIS-006 · Tenant routing: BANK-AXIS-009

API reference

Five endpoints, one resource model

POST/v1/bank-generations

Create a bank-generation operation

This 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.

Request body

FieldRequirementMeaning
specIdRequired UUIDVerbatim Content test_spec ID.
bankModeRequired enumsame_blueprint_fixed_forms or qti_adaptive_selection.
formCountConditional integerFixed only: positive and at least the spec's min_forms; no AlphaTest-specific upper cap is pinned. Forbidden for adaptive.

Response

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.

Adaptive request · contract requires 424 while uncertified
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}'
Current branch: HTTP 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

POST/v1/legacy-bank-imports

Verify the governed 17-form collection in place

This 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.

Shell
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.

Never assign this result. A successful immutable replay may return 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

GET/v1/bank-operations

List and reconcile operations

Requires bank:read. Filters combine with AND. Use opaque cursor paging; never parse a cursor or reuse it with changed filters or sort.

QueryAllowed shape
statusOne exact OperationStatus.
kindgeneration or legacy_import.
specId, bankIdExact Content UUID.
sort-createdAt (default), createdAt, -updatedAt, or updatedAt.
limit1–100; default 25.
cursorOpaque, 1–2048 characters.
modifiedSinceInclusive RFC 3339 updatedAt bound with UTC offset.
Shell
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

GET/v1/bank-operations/{operationId}

Get one operation snapshot

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.

Shell
curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer $ALPHATEST_TOKEN" \
  "$ALPHATEST_BANK_URL/v1/bank-operations/$OPERATION_ID"
Ready fixed-form result shape
{
  "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

GET/v1/bank-operations/{operationId}/manifest

Download immutable verification evidence

Requires 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.

Shell
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"

Adaptive operations must add one verified pool receipt

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.

Receipt, not replica. QTI owns root and member order; Content owns KC relationships. Bank must not require CASE/KC arrays on QTI item refs or infer KC from titles. Adaptive admission is closed until a credentialed replay proves this receipt. Fixed-form manifests omit adaptivePool.

Decision: one immutable manifest subresource · Adaptive receipt: BANK-ITD-017 · Every nested key: manifest schema

Errors

One stable problem shape

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.

StatusStable codes
400validation_failed
401authentication_required
403insufficient_scope, tenant_mismatch
404resource_not_found
409idempotency_conflict, spec_contract_violation, manifest_not_ready
415 / 424 / 429unsupported_media_type, dependency_contract_unavailable, rate_limited
500internal_error
Required create-route 424 shape
{
  "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.

Audit-grade receipts

“Ready” is a proof-backed state

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.

01Slot fit

Standard, DOK, and requested interaction match.

02Diagnostic distractors

Every wrong selected response retains a distinct Incept-authored misconception tag.

03QTI validity

Deterministic QTI 3.0 package is ingested and read back.

04Score truth

Expected response scores correctly in Platform3.

05Owner proof

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

Explicit limits

No quiet downgrade, no local substitute

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.

BoundaryWhat the manifest tells youAssignment outcome
inceptGeneration capability or invalid response gap.ready_with_gaps is always assignable=false; otherwise the operation fails closed.
converterJSON semantics cannot be represented faithfully in QTI 3.0.
platform3_qtiIngest or runtime-score proof failed.
platform3_caseRequired CFItem could not be resolved.
platform3_contentSpec or bank membership contract failed.
Not in v1: item authoring endpoints, AlphaTest-hosted item browsing, mutable operations, webhooks, or public deletion. Bank/form/item reads remain Platform3 Content/QTI responsibilities. Polling plus modifiedSince is the eventing contract.
Security & ownership

Thin by design

AlphaTest stores
  • Tenant-scoped operation metadata
  • Upstream IDs, hashes, statuses
  • Idempotency and evidence manifests
AlphaTest never stores
  • Item bodies or answer keys
  • Rosters, learners, responses
  • Attempts, scores, mastery state

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

Conformance · mode-by-mode

Adopt certified fixed forms; hold adaptive and public legacy

The approved owner receipt opens fixed-form generation only. Adaptive and legacy remain independent proof branches:

  • Fixed certified: 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.
  • Adaptive closed: new public creates return typed 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.
  • Security contract: anonymous access rejects; tenant identity is derived from the verified JWT; missing and cross-tenant resources share the same 404 shape.
  • Legacy verification-only: public admission remains typed-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

Compatibility

Stable under /v1

Additive 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