TimeBack Platform 1EdTech API Reference

A single-page reference for the platform substrate: mint a demo token, inspect module release state, create a tenant, read it back, and verify audit and idempotency evidence against the live implementation.

Live implementation route Platform 1EdTech under reconciliation Derived from approved architecture and dictionary
No sections match that search.

Quickstart

Copy this block into a shell with curl and jq. It uses consumer-chosen input values and the public demo token helper, then walks the exact reviewer job end to end.

Demo flow: mint token, list modules, create tenant, read tenant, audit, and idempotency state
export PLATFORM_BASE_URL="${PLATFORM_BASE_URL:-https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/implementation/api}"
export TOKEN="$(curl -fsS -X POST "$PLATFORM_BASE_URL/dev/mint?tenantId=demo" | jq -r .token)"

export TENANT_SUFFIX="$(date +%s)-$RANDOM"
export TENANT_KEY="docs-$TENANT_SUFFIX"
export IDEMPOTENCY_KEY="tenant-create-$TENANT_SUFFIX"

curl -fsS "$PLATFORM_BASE_URL/platform/modules" \
  -H "Authorization: Bearer $TOKEN" | jq '.data[] | {key,status,surfaces}'

CREATE_RESPONSE="$(curl -fsS -X POST "$PLATFORM_BASE_URL/platform/tenants" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -H "Content-Type: application/json" \
  --data "{\"tenantKey\":\"$TENANT_KEY\",\"displayName\":\"Docs Demo $TENANT_SUFFIX\",\"metadata\":{\"source\":\"customer_website\"}}")"

export TENANT_ID="$(printf '%s' "$CREATE_RESPONSE" | jq -r .tenantId)"
export TENANT_ID_ENCODED="$(printf '%s' "$TENANT_ID" | jq -sRr @uri)"
export IDEMPOTENCY_KEY_ENCODED="$(printf '%s' "$IDEMPOTENCY_KEY" | jq -sRr @uri)"
printf '%s\n' "$CREATE_RESPONSE" | jq '{tenantId,tenantKey,displayName,status,links}'

curl -fsS "$PLATFORM_BASE_URL/platform/tenants/$TENANT_ID_ENCODED" \
  -H "Authorization: Bearer $TOKEN" | jq '{tenantId,tenantKey,status}'

curl -fsS "$PLATFORM_BASE_URL/platform/tenants/$TENANT_ID_ENCODED/audit-log?limit=5" \
  -H "Authorization: Bearer $TOKEN" | jq '.data[0] | {action,outcome,resourceType,resourceId,idempotencyKeyId}'

curl -fsS "$PLATFORM_BASE_URL/platform/tenants/$TENANT_ID_ENCODED/idempotency-keys/$IDEMPOTENCY_KEY_ENCODED" \
  -H "Authorization: Bearer $TOKEN" | jq '{status,idempotencyKey,responseStatus,resourceType,resourceId}'
Expected demo result. Tenant creation returns a signed demo_... tenant id. That id is URL-safe after encoding and can be read only with the public demo token. Real reviewer tokens must not read demo handles.

Base URLs

NameValue
PLATFORM_BASE_URL https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/implementation/api
Problem type root https://platform3-andymontgomery-9773s-projects.vercel.app/problems
Documentation route https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/customer_website

Endpoint examples append paths such as /platform/modules to PLATFORM_BASE_URL. Problem type URIs always use the master problem catalog.

Authentication

Every customer API route except the service root, demo-token helper, CORS preflight, and Problem catalog requires a signed Bearer JWT. The token must contain sub, tenantId or tenant_id, role or roles, and exp. Real-student apps also receive personId and studentId from the Platform-owned login bridge.

Credential pathUse it forHow to get itProvenance
Public demo token Cold docs testing on tenantId=demo. Can create signed demo_... tenant handles. POST $PLATFORM_BASE_URL/dev/mint?tenantId=demo. The helper ignores requested roles and TTL; it returns roles writer, auditor, demo and expires in 900 seconds. PITD-025
Reviewer or production JWT Real tenant operations against the same base URL. Reviewer tokens carry review authority for non-demo tenant UUIDs. Set TOKEN="$PLATFORM_REVIEWER_JWT" when the driver has seeded it, or use the out-of-band tenant token supplied by the operator. There is no public real-tenant mint endpoint. PITD-005
TimeBack browser session Real alphatimeback3 student, guardian/parent, and guide sessions. Apps redirect to GET /platform/tenants/{tenantId}/auth/timeback/login. The first-party bridge uses Cognito Hosted UI code+PKCE, then an opaque Platform cookie can mint a 600-second Bearer JWT from POST .../session-token. Cognito tokens are discarded; apps do not host login UI, collect passwords, or receive refresh tokens. PITD-025
TimeBack session contract. For the existing active alphatimeback3 tenant, Cognito verifies the credential, People & Orgs owns current roles/relationships, and Platform owns the exact operator-created subject binding, local session, JWT, and revocation. There is no email auto-binding. Unbound subjects fail closed. The opaque session is 30 minutes idle / 2 hours absolute; only its SHA-256 handle hash is stored. Each token mint re-reads People & Orgs and returns a 600-second HS256 Bearer JWT with the tenant row's existing unique tenant_key, no aud, and fresh claims. The auth URL, binding, session, and audit stay on the canonical tenant UUID. Logout revokes the session and the app discards cached JWTs. Customer APIs never accept the cookie.
Operator boundary. The legacy Cognito invite/AdminCreateUser action remains an operator action outside Platform3. Once it returns the exact Cognito sub, an authorized operator binds issuer + subject + tenant UUID + People & Orgs person_id through the endpoint below. Platform3 embeds no AWS admin SDK and runs no invitation workflow.
Real-tenant reviewer setup
export PLATFORM_BASE_URL="https://platform3-andymontgomery-9773s-projects.vercel.app/platform/1edtech/implementation/api"
export TOKEN="$PLATFORM_REVIEWER_JWT"
curl -fsS "$PLATFORM_BASE_URL/platform/modules" \
  -H "Authorization: Bearer $TOKEN" | jq '.data[].key'
Tenant routing. Tenant resources use the tenant id in the URL and enforce it against the JWT tenant claim. Demo handles are a special public-demo form and cannot be read by reviewer or production JWTs.

End-to-end Flow

This is the operational path this page supports without out-of-band setup.

1. MintGet a short-lived demo Bearer token from /dev/mint?tenantId=demo.
2. DiscoverCall /platform/modules and choose approved module docs.
3. CreateCreate a tenant with Idempotency-Key and save tenantId.
4. ReadRead tenant detail from the tenant URL under the same token.
5. InspectRead audit and idempotency rows to verify stored evidence.
6. Handle errorsBranch on stable Problem code and type values.

Endpoint Reference

Every endpoint below has request shape, response shape, status codes, copy-paste curl, and provenance. Paths are relative to PLATFORM_BASE_URL unless stated otherwise.

Service Root

GET/
No auth

Returns the service name, surface, route resolution rule, endpoint paths, and canonical documentation links.

Operation id
platform.service.root
Auth
None
Provenance
PITD-011, PITD-006

Response fields

FieldTypeMeaning
servicestringHuman-readable API name.
modulestringplatform.
surfacestring1edtech.
endpointsobjectRelative endpoint paths documented on this page.
linksobjectArchitecture, data dictionary, and customer website URLs.

Status codes

200Service root returned.
curl
curl -fsS "$PLATFORM_BASE_URL" | jq '{service,module,surface,endpoints}'

Mint Demo Token

POST/dev/mint?tenantId=demo
No auth

Mints a short-lived token for the public demo tenant. It is intentionally restricted to tenantId=demo; non-demo token issuance is protected operator work.

Operation id
platform.dev.mintDemoToken
Request body
None
Query
tenantId must be exactly demo.
Provenance
PITD-025

Response fields

FieldTypeMeaning
tokenstringHS256 JWT. Store it in a shell variable; do not paste it into public artifacts.
tokenTypestringBearer.
expiresInintegerSeconds until expiry; currently 900.
tenantIdstringdemo.
rolesarray<string>writer, auditor, and demo.

Status codes

200Token minted.
400Missing tenantId.
403tenantId is not demo.
405Use POST.
curl
export TOKEN="$(curl -fsS -X POST "$PLATFORM_BASE_URL/dev/mint?tenantId=demo" | jq -r .token)"
Example response
{
  "token": "<jwt>",
  "tokenType": "Bearer",
  "expiresIn": 900,
  "tenantId": "demo",
  "roles": ["writer", "auditor", "demo"]
}

List Modules

GET/platform/modules
Bearer token

Lists registered Platform3 modules, their current loop-derived release status, audience, and documentation URLs.

Operation id
platform.modules.list
Auth
Any valid platform Bearer token.
Provenance
module_key enum, module_release_status enum, module release convention

Response fields

FieldTypeMeaning
dataarray<Module>Module registry rows. See Module object.
links.dataDictionaryURLDeep link to module key dictionary provenance.

Status codes

200Registry returned.
401Missing, malformed, expired, or invalid Bearer token.
curl
curl -fsS "$PLATFORM_BASE_URL/platform/modules" \
  -H "Authorization: Bearer $TOKEN" | jq '.data[] | {key,status,surfaces}'

Get Module Detail

GET/platform/modules/{moduleKey}
Bearer token

Reads one module registry row by moduleKey, such as qti, oneroster, caliper, case, incept, or platform.

Operation id
platform.modules.get
Path params
moduleKey must be a published module_key.
Auth
Any valid platform Bearer token.

Response fields

ModuleobjectSame shape as entries in GET /platform/modules.

Status codes

200Module returned.
401Missing or invalid Bearer token.
404No registry row exists for the module key.
curl
curl -fsS "$PLATFORM_BASE_URL/platform/modules/case" \
  -H "Authorization: Bearer $TOKEN" | jq '{key,status,surfaces}'

List Producer Surfaces

GET/platform/producer-surfaces
Bearer token

Lists governed upstream producer surfaces. This is the Platform3-native registry for producer rows that feed canonical Content/QTI, Results, Events, or Analytics materializations.

Operation id
platform.producer_surfaces.list
Auth
demo, reviewer, auditor, support, writer, service, platform:producer-surface:read, platform:incept:read, incept:read, or platform:*.
Provenance
PITD-032, Incept producer-surface dictionary

Response fields

dataarray<ProducerSurface>Governed producer-surface registrations. See ProducerSurface object.
linksobjectArchitecture and data-dictionary provenance links.

Status codes

200Registry returned.
401Missing or invalid Bearer token.
403Token lacks read authority.
curl
curl -fsS "$PLATFORM_BASE_URL/platform/producer-surfaces" \
  -H "Authorization: Bearer $TOKEN" | jq '.data[] | {key,kind,status,api}'

Get Incept Producer Surface

GET/platform/producer-surfaces/incept
Bearer token

Returns the governed Incept registration, the approved object list, and the Platform API paths that replace temporary direct SUPABASE_DB_URL reads.

Operation id
incept.producer_surface.get
Auth
Same as GET /platform/producer-surfaces.
Provenance
PITD-032

Response fields

ProducerSurfaceobjectSee ProducerSurface object.

Status codes

200Registration returned.
401Missing or invalid Bearer token.
403Token lacks read authority.
curl
curl -fsS "$PLATFORM_BASE_URL/platform/producer-surfaces/incept" \
  -H "Authorization: Bearer $TOKEN" | jq '{key,objects,api,deprecatedBootstrapPath}'

List Incept Progress

GET/platform/producer-surfaces/incept/progress
Bearer token

Reads the Platform-owned progress projection backed by incept.incept_progress_projection_input_v and the current projection checkpoint. Use this for Incept customer progress/status instead of direct database access.

Operation id
incept.progress.read
Query params
limit (1-10000 for compact customer progress; 1-100 for raw customer-generation ledger evidence), optional skillId, optional loopId, optional contractId, optional projectionName, and runtime filters eventType, subject, gradeLevel, contentType, contentSubtype, trafficClass (customer, goal-run, or self-test), occurredSince, and occurredUntil. Omitting contractId/projectionName defaults to the compact incept-customer-progress-v1 / generation-loop-progress-report customer-progress read model. Raw incept-customer-generation-ledger-v1 reads must include at least one narrowing filter before payload details are extracted. tenantId may be supplied only when it matches the JWT tenant or the caller has service authority.
Auth
demo, reviewer, auditor, support, service, platform:producer-surface:read, platform:incept:read, incept:read, or platform:*.
Audit
UUID-tenant reads write platform.audit_log with module=incept, operationId=incept.progress.read, and action=read_privileged.
Provenance
progress view, checkpoint table

Response fields

dataarray<InceptProgress>Progress rows. See InceptProgress object.
checkpointsarray<InceptProjectionCheckpoint>Current checkpoint rows. See InceptProjectionCheckpoint object.
filtersobjectApplied tenant, skill, loop, contract, projection, and limit filters.
linksobjectDictionary and architecture provenance links.

Status codes

200Progress returned.
400Invalid filter or unsafe raw-ledger limit. Use the compact incept-customer-progress-v1 / generation-loop-progress-report projection for customer progress pages, and keep raw generation-ledger evidence reads filtered and bounded.
401Missing or invalid Bearer token.
403Token lacks read authority or tenant scope.
curl
curl -fsS "$PLATFORM_BASE_URL/platform/producer-surfaces/incept/progress?limit=100&contractId=incept-customer-progress-v1&projectionName=generation-loop-progress-report" \
  -H "Authorization: Bearer $TOKEN" | jq '{rows:(.data|length), checkpoint:.checkpoints[0].status, first:.data[0]}'

List Incept Projection Checkpoints

GET/platform/producer-surfaces/incept/projection-checkpoints
Bearer token

Reads materialization checkpoints for ledger-backed Incept projections.

Operation id
incept.projection_checkpoints.read
Query params
limit (1-100). tenantId follows the same tenant-scope rule as Incept progress.
Audit
UUID-tenant reads write platform.audit_log with module=incept and operationId=incept.projection_checkpoints.read.

Response fields

dataarray<InceptProjectionCheckpoint>Checkpoint rows. See InceptProjectionCheckpoint object.
linksobjectDictionary and architecture provenance links.

Status codes

200Checkpoints returned.
401Missing or invalid Bearer token.
403Token lacks read authority or tenant scope.
curl
curl -fsS "$PLATFORM_BASE_URL/platform/producer-surfaces/incept/projection-checkpoints?limit=5" \
  -H "Authorization: Bearer $TOKEN" | jq '.data[] | {projectionName,status,materializedAt,sourceEventCount}'

Create Tenant

POST/platform/tenants
Bearer token + Idempotency-Key

Creates a tenant row or, in the public demo flow, a signed demo tenant handle that can be read back without relying on function-instance memory.

Operation id
platform.tenants.create
Required roles/scopes
service, writer, demo, reviewer, platform:tenant:create, or platform:*. Creating directly in active additionally requires the exact platform-operator tenant principal, the service role, and platform:tenant:create or platform:* scope; every tenant-bound service credential and all other create authorities remain provisioning-only.
Headers
Authorization: Bearer $TOKEN, Idempotency-Key, Content-Type: application/json.
Provenance
PITD-003, PITD-024, platform.tenant

Request fields

FieldTypeRequiredMeaning
tenantKeystringYesLowercase slug, 3 to 64 characters, unique for stored real tenants.
displayNamestringYesCustomer-facing name, 1 to 160 visible characters.
metadataobjectNoSmall redacted operational facts. Do not include credentials, raw JWTs, learner/parent PII, IP addresses, or contact data.
statusstringNoDefaults to provisioning. The exact platform-operator tenant principal with Platform service role and explicit tenant-create scope may send active when authentication and operational setup are complete.

Response fields

FieldTypeMeaning
tenantIdstringSigned demo_... handle for public demo tokens, UUID for stored reviewer/production tenants.
tenantKeystringEcho of the accepted slug.
displayNamestringAccepted display label.
statusstringprovisioning by default, or active for an authorized one-step operator onboarding. See tenant_status.
metadataobjectRedacted metadata object.
createdAt, updatedAtstringUTC timestamps.
linksobjectArchitecture and dictionary provenance links for tenant semantics.

Status codes

201Tenant created. Header idempotency-replayed is false.
200Same idempotency key and same request replayed. Header idempotency-replayed is true.
400Invalid slug, display name, body, or malformed Idempotency-Key.
401Missing or invalid Bearer token.
403Token lacks a required role or scope.
409Same Idempotency-Key reused with different content or tenant key conflict.
428Missing Idempotency-Key; returns precondition_required.
curl
curl -fsS -X POST "$PLATFORM_BASE_URL/platform/tenants" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  -H "Content-Type: application/json" \
  --data "{\"tenantKey\":\"$TENANT_KEY\",\"displayName\":\"Docs Demo $TENANT_SUFFIX\",\"metadata\":{\"source\":\"customer_website\"}}" | jq

TimeBack Browser Session

GET/platform/tenants/{tenantId}/auth/timeback/login
First-party browser only

Starts Cognito Hosted UI authorization code + PKCE for an explicitly allowlisted first-party tenant and HTTPS origin. The canonical tenant UUID in the route selects one exact redirect/post-login/logout tuple; the request Host and Origin validate that choice; neither selects it. callback validates signed issuer/audience/nonce/signature claims and refuses any subject without an active operator binding. Invalid, duplicate, mismatched, unlisted, or absent configuration returns a typed 503 end_user_auth_not_configured before storage; it never falls back to email or an app credential.

Method and pathAuthorityResult
GET .../loginConfigured first-party host302 to Cognito with state, nonce, and S256 PKCE challenge.
GET .../callbackSigned login cookie + one-use Cognito code302 to configured app and opaque HttpOnly session cookie; no Cognito token is retained.
POST .../session-tokenExact first-party Origin + active session cookie201 with a 600-second HS256 Bearer JWT after fresh People & Orgs authorization. No aud.
POST /platform/tenants/{tenantId}/token-contextThe issued end-user JWT as Authorization: Bearer200 no-store verified subject, roles, scopes, person/relationship claims, and expiry; never echoes the token.
POST .../logoutExact first-party Origin + optional session cookieRevokes local session, clears cookie, and redirects through Cognito logout.

The auth URL, identity binding, local session, and audit always use the canonical active Platform tenant UUID. At session-token, Platform loads that tenant row and signs its existing unique platform.tenant.tenant_key into downstream tenantId/tenant_id because People & Orgs and Results already route and store by tenant key. This bounded handoff adds no repository-wide UUID adapter, additional tenant claim, or second mapping table. Human tokens carry only exact people/relationship ids—personIds contains authorized students, never the parent or guide actor, and there are no place, school, or class container grants. A role with no eligible student scope fails closed.

Operator configuration is explicit: shared PLATFORM_TIMEBACK_COGNITO_ISSUER, PLATFORM_TIMEBACK_COGNITO_HOSTED_DOMAIN, public PLATFORM_TIMEBACK_COGNITO_CLIENT_ID, PEOPLE_ORGS_BASE_URL, and the existing PLATFORM_JWT_SIGNING_SECRET; plus PLATFORM_TIMEBACK_TENANT_ROUTES_JSON, a strict array whose entries contain exactly tenantId, redirectUri, postLoginUri, and logoutUri. Every URI in one entry shares the same HTTPS origin and the callback path contains that entry's canonical tenant UUID. The legacy single-tenant URI variables remain supported only when the allowlist is absent. Platform mints the bounded People & Orgs service token from the existing signing key; there is no second long-lived service-token setting.

Each consumer reverse-proxies only its exact Platform auth paths on its own public origin. The session cookie therefore remains host-only and SameSite, and the app receives no issuer credential, signing secret, or cross-site cookie. Both callback URLs must be registered on the shared Cognito public client.

A server-side consumer such as AP One keeps the JWT opaque and calls POST /platform/tenants/{canonicalTenantUuid}/token-context with it as the Bearer credential. Platform reuses its existing JWT verifier and resolves that canonical UUID only to the active row's existing tenant key. The raw token must contain both tenantId and tenant_id, exactly equal to that key; workspaceId/workspace_id cannot rescue or broaden tenant scope. Raw roles and scopes are unique nonempty string arrays, scalar role equals roles[0], scopes are exactly people_orgs:read and results:read, and integer exp - iat is exactly 600. Parent/guardian relationships require a parent or guardian role; guide relationships require the guide role; legitimate multi-role tokens remain valid. Unknown, container-grant, and extra privilege claims fail closed. The endpoint mints nothing, stores nothing, never echoes the token, and does not give the app the symmetric signing key.

Exact 200 response. Only relationship fields applicable to the verified roles are present; personIds is the exact union of authorized student ids.

{
  "active": true,
  "tokenType": "end_user",
  "tenantId": "<tenant_key>",
  "tenant_id": "<same tenant_key>",
  "platformTenantId": "<canonical tenant UUID>",
  "subject": "timeback:cognito:<32 lowercase hex>",
  "roles": ["student"],
  "scopes": ["people_orgs:read", "results:read"],
  "issuedAt": 123,
  "expiresAt": 723,
  "claims": {
    "personId": "<actor person id>",
    "studentId": "<student actor only; otherwise omitted>",
    "studentIds": ["<student actor only; otherwise omitted>"],
    "agentOf": ["<parent/guardian student; otherwise omitted>"],
    "guardianOf": ["<same ids as agentOf; otherwise omitted>"],
    "guideOf": ["<guide student; otherwise omitted>"],
    "personIds": ["<exact authorized-student union>"],
    "authProvider": "timeback-cognito"
  }
}
StatusCodeBoundary
400invalid_requestRoute tenant is not a canonical Platform UUID.
401unauthorizedBearer is missing, malformed, tampered, wrong-issuer, expired, missing/malformed exp, future-issued, or has neither tenant alias.
403tenant_scope_mismatchA signed token has a missing, conflicting, or wrong tenant alias, or includes a workspace alias.
403forbiddenTenant is inactive, or the signed token fails the exact end-user role, scope, lifetime, person-scope, relationship-provenance, or claim allowlist contract.
404not_foundCanonical tenant UUID does not exist.
405method_not_allowedAn authenticated request uses a method other than POST; Allow: POST.
TenantCanonical UUIDInitial first-party route tuple
alphatimeback35ebda6a8-91f1-4685-a055-146c035b9e22https://alphatimeback3-andymontgomery-9773s-projects.vercel.app; callback under /platform/tenants/5ebda6a8-91f1-4685-a055-146c035b9e22/auth/timeback/callback, then /home; logout to /sign-in.

AP One is intentionally absent until its production app origin exists and the active tenant create is read back. Its deterministic onboarding request is expected to return 723c3d8b-274a-4f6c-8b46-df92951348eb, but that reservation is not evidence of a live tenant or route tuple. https://apone.inceptstore.com is the current staging review origin and must not be used in the production allowlist or real-student callback path.

Bind or Revoke a TimeBack Identity

POST/platform/tenants/{tenantId}/auth/timeback/identity-bindings
Operator Bearer

After the legacy invite returns a Cognito subject, create the one durable binding. The exact body is {"subject":"...","person_id":"..."}. Platform derives the canonical tenant from the route and the issuer from the Cognito configuration; People & Orgs must return a real, enabled, non-deleted, lifecycle-current person. Exact retries return 200; conflicting active subject/person bindings return 409.

Create authority
operator role, or explicit platform:end-user-auth:bind/platform:* scope, in the same tenant.
Revoke path
POST /platform/tenants/{tenantId}/auth/timeback/identity-bindings/{bindingId}/revoke with platform:end-user-auth:revoke authority. Revocation closes every linked browser session.
Not accepted
Email, username, tenant key/alias, inferred guardian relationship, Cognito token, or extra body fields.

Exchange Tenant Token

POST/platform/tenants/{tenantId}/token-exchanges
Bearer issuer token

Mints a short-lived real-tenant JWT for an already-authorized issuer/service integration. An allowlisted first-party browser bridge uses its narrower session-token route so it can re-derive People & Orgs claims on every mint; apps still do not own login UI, passwords, refresh tokens, or logout state.

Operation id
platform.tokens.exchange
Path params
tenantId must be the stored real tenant UUID and must match the issuer token tenant unless the issuer is a cross-tenant platform service token.
Required roles/scopes
Same-tenant issuer role or platform:token:issue scope; cross-tenant issuance requires service plus platform:token:issue.
Headers
Authorization: Bearer $ISSUER_TOKEN, Content-Type: application/json.
Provenance
PITD-025, student login token exchange

Request fields

FieldTypeRequiredMeaning
subjectstringYesJWT sub. For student sessions use a pseudonymous TimeBack subject such as timeback:student:person_student_001, not a name or email.
rolesarray<string> or comma stringNoDefaults to reader. Student sessions send ["student"].
scopesarray<string> or comma stringNoModule scopes the app needs, for example people_orgs:read, content:read, qti:attempt, and events:write.
ttlSecondsintegerNoDefaults to 600. Must be 60 through 3600 seconds.
claimsobjectNoAllowlisted identity claims only: personId, studentId, agentOf, personIds, placeIds, schoolIds, classIds, candidateRef, and authProvider. For student tokens, personId and studentId are the same Alpha People & Orgs person id.

Response fields

FieldTypeMeaning
tokenstringHS256 JWT containing sub, tenantId/tenant_id, role/roles, scopes, allowlisted claims, iat, and exp.
tokenTypestringBearer.
expiresInintegerGranted lifetime in seconds.
tenantId, tenant_idstringTenant UUID copied into the JWT tenant claims.
subjectstringJWT subject.
roles, scopes, claimsarrays / objectAccepted authorization and identity shape. The audit row stores hashes/counts, not raw person ids.

Status codes

201Token minted and redacted audit row written.
400Invalid body, subject, TTL, role/scope list, unsupported claim, PII-shaped claim, or mismatched personId/studentId.
401Missing or invalid issuer Bearer token.
403Issuer lacks platform:token:issue authority or tenant scope.
404Stored tenant UUID is absent.
curl
curl -fsS -X POST "$PLATFORM_BASE_URL/platform/tenants/$REAL_TENANT_ID/token-exchanges" \
  -H "Authorization: Bearer $REAL_TENANT_ISSUER_JWT" \
  -H "Content-Type: application/json" \
  --data '{
    "subject": "timeback:student:person_student_001",
    "roles": ["student"],
    "scopes": ["people_orgs:read", "content:read", "qti:attempt", "events:write"],
    "claims": {
      "personId": "person_student_001",
      "studentId": "person_student_001",
      "personIds": ["person_student_001"],
      "schoolIds": ["school_alpha"],
      "classIds": ["ap_lang_2026"],
      "authProvider": "timeback-legacy"
    },
    "ttlSeconds": 600
  }' | jq '{tokenType,expiresIn,tenantId,subject,roles,scopes,claims}'

Get Tenant

GET/platform/tenants/{tenantId}
Bearer token

Reads the tenant resource visible to the current token. URL-encode the full tenant id, especially signed demo handles.

Operation id
platform.tenants.get
Path params
tenantId from tenant creation response.
Provenance
PITD-028, tenant_id

Response fields

TenantobjectSame response shape as POST /platform/tenants.

Status codes

200Tenant returned.
401Missing or invalid Bearer token.
403Token tenant scope does not match the route tenant, or reviewer token attempted to read a demo handle.
404Tenant is absent or not visible as a stored UUID tenant.
curl
curl -fsS "$PLATFORM_BASE_URL/platform/tenants/$TENANT_ID_ENCODED" \
  -H "Authorization: Bearer $TOKEN" | jq '{tenantId,tenantKey,status,links}'

List Tenant Audit Log

GET/platform/tenants/{tenantId}/audit-log?limit=20
Bearer token

Returns recent append-only audit rows for one tenant. This is the operational proof that tenant creation wrote a redacted audit narrative.

Operation id
platform.tenants.auditLog.list
Required roles/scopes
service, support, auditor, demo, reviewer, platform:audit:read, or platform:*.
Query
limit is optional and clamped by the implementation to a safe range.
Provenance
PITD-009, platform.audit_log

Response fields

FieldTypeMeaning
dataarray<AuditLog>Audit events ordered newest first. See AuditLog object.
links.dataDictionaryURLDeep link to the audit-log table.

Status codes

200Audit rows returned. Empty data means no visible audit rows in scope.
401Missing or invalid Bearer token.
403Token lacks audit read authority or tenant scope.
404Stored tenant UUID is absent.
curl
curl -fsS "$PLATFORM_BASE_URL/platform/tenants/$TENANT_ID_ENCODED/audit-log?limit=5" \
  -H "Authorization: Bearer $TOKEN" | jq '.data[] | {action,outcome,operationId,resourceType,requestId,traceId}'

Get Idempotency Key

GET/platform/tenants/{tenantId}/idempotency-keys/{idempotencyKey}
Bearer token

Reads the retry ledger row for tenant creation. A completed row proves that retries will replay instead of duplicating work.

Operation id
platform.tenants.idempotencyKeys.get
Required roles/scopes
service, support, writer, demo, reviewer, platform:idempotency:read, or platform:*.
Provenance
PITD-024, platform.idempotency_key

Response fields

FieldTypeMeaning
idempotencyKeyIdstringRetry ledger row id.
tenantIdstringTenant scope for the retry key.
module, surface, method, routeTemplate, operationIdstringThe exact replay scope.
idempotencyKeystringCaller-supplied header value.
requestHashstringCanonical hash used to distinguish replay from conflict.
statusstringReplay lifecycle. Demo tenant creation should return completed.
responseStatus, responseBodyinteger, objectStored safe response for replayable outcomes.
resourceType, resourceIdstringPrimary resource created by the operation.
firstRequestIdstringRequest id of the first request that claimed this key, used for support and audit correlation.
lockedUntilstring or nullTemporary lock deadline while a request is in progress; final completed rows return null.
createdAt, updatedAt, expiresAtstringUTC timestamps for retry-window auditing.

Status codes

200Idempotency row returned.
401Missing or invalid Bearer token.
403Token lacks authority or tenant scope.
404No idempotency row is visible for that tenant/key/scope.
curl
curl -fsS "$PLATFORM_BASE_URL/platform/tenants/$TENANT_ID_ENCODED/idempotency-keys/$IDEMPOTENCY_KEY_ENCODED" \
  -H "Authorization: Bearer $TOKEN" | jq '{status,idempotencyKey,responseStatus,resourceType,resourceId}'

Problem Type Catalog

GET/problemsor/problems/{problemPath}
No auth

Documents stable RFC 7807 Problem type URIs. The catalog is available at the master root and through the implementation route.

Operation id
platform.problems.list, platform.problems.get
Accept
application/json for JSON docs; HTML is also available for a single problem type.
Provenance
PITD-027

Status codes

200Problem index or problem document returned.
curl
curl -fsS "https://platform3-andymontgomery-9773s-projects.vercel.app/problems" \
  -H "Accept: application/json" | jq '.problemTypes[] | {type,code,status}'

Module Object

FieldTypeMeaning
keystringStable module_key.
namestringDisplay name.
statusstringLoop-derived module_release_status.
jobstringWhat the module owns.
surfacesarray<Surface>Published surfaces and docs for the module.

Surface fields

FieldMeaning
code1edtech, alpha, platform, or incept where documented.
statusRelease status for that surface.
audienceWho the surface is built for.
customerUrl, dataDictionaryUrl, architectureUrl, qcUrlCanonical docs. qcUrl appears after Surface QC is published.
releaseNoteOptional note for reconciliation or approval context.

ProducerSurface Object

The Incept producer surface is the governed Platform registration for generation-loop provenance. It is not a replacement Content, QTI, Results, Events, or Analytics store; consumer reads use the Platform API paths below.

FieldTypeMeaning
keystringStable producer key. For this release the only row is incept.
kindstringupstream_producer_surface; the surface preserves provenance and projects progress but does not serve student-facing generated artifacts directly.
statusstringLoop-derived release state. under_reconciliation means the registration is intentional and visible while the Platform surface finishes approval.
decisionstringPITD-032, the architectural decision that makes Incept first-class.
deprecatedBootstrapPathstringDirect SUPABASE_DB_URL reads from Incept apps are bootstrap-only and must be replaced by the Platform API/view path for customer progress.
legacyCompatibilityObjectstringpublic.incept_generation_runs remains compatibility-only; new durable objects belong under the governed incept schema.
apiobjectCanonical read routes: self, progress, and projectionCheckpoints.
objectsarray<string>Every governed Incept table/view covered by the Platform data dictionary.
linksobjectArchitecture, data dictionary, and issue provenance.

InceptProgress Object

Each row is a Platform API projection over the governed Incept ledger/projection contract. Materialized customer-progress rows use the indexed ledger fast path; general repair-evidence reads can use the normalized projection view. Field semantics are mastered by the Platform data dictionary. Headline Accuracy counts only trafficClass=customer; goal-run and self-test rows stay visible as drilldown lanes.

FieldTypeMeaning
eventIdstringCanonical ledger event identity.
tenantIdstring or nullTenant from incept.incept_event_ledger; null rows are global/backfilled Incept evidence.
loopRunIdstring or nullGeneration loop run associated with the event.
loopIdstring or nullLoop or customer-progress grouping key.
skillIdstring or nullSkill or skill-pack identifier when supplied by the source event.
contentType, contentSubtypestring or nullGenerated content grouping from the source event.
qualityBarId, qualityBarVersionstring / integer or nullQuality bar that gives score units, target semantics, and pass/fail meaning.
eventTypestringLedger event type included in the progress projection.
eventStatus, projectionStatusstring or nullStatus terms projected from the event payload.
currentScore, targetScorenumber or nullProgress or quality score values parsed from the event payload; units come from the linked quality bar or evaluator.
customerAvailable, qualityClearedboolean or nullWhether the artifact/status is available to the customer and whether the quality gate cleared.
promotionAccepted, promotedVersionId, promotedScoreboolean/string/number or nullPromotion decision facts joined from incept.incept_promotion_decisions.
repairAttemptId, repairStatusstring or nullRepair attempt facts joined from incept.incept_repair_attempts.
trafficClassstringGoverned Incept traffic class: customer, goal-run, or self-test. Missing legacy ledger payload values default to customer; new owner-directed materialization runs must write goal-run and loop self-tests must write self-test.
occurredAt, recordedAtstring or nullSource event time and ledger record time.
detailsobjectRedacted payload fields needed by customer progress consumers: contractId, projectionName, trafficClass, title, skillTitle, blockingReason, subject, gradeLevel, contentType, contentSubtype, dimensionVector, source report ids, capability numerator/denominator, accuracy numerator/denominator, held-out numerator/denominator, top-level eval numerator/denominator, and parsed quality/progress scores.

InceptProjectionCheckpoint Object

Each row is the materialization checkpoint that makes an Incept projection reproducible from the ledger. A cold agent can replay from the ledger up to the checkpoint watermark and compare with the API response.

FieldTypeMeaning
projectionNamestringProjection identity, such as generation-loop-progress-report.
projectionVersionintegerVersion of the projection rule.
checkpointKeystringPartition key for the checkpoint.
lastEventIdstring or nullLast included ledger event.
lastEventRecordedAt, lastEventOccurredAtstring or nullLedger record and source-event time for the last included event.
sourceEventCountintegerNumber of source ledger events included in the materialized projection.
materializedAtstring or nullWhen the projection checkpoint was written.
outputPathstring or nullOptional generated output pointer for projection artifacts.
statusstringProjection status, for example current.
summary, watermarkobjectSafe structured checkpoint metadata used for replay/readback comparison.

AuditLog Object

Every row in GET /platform/tenants/{tenantId}/audit-log is the API projection of one append-only platform.audit_log row. The endpoint envelope adds links.dataDictionary; the fields below are the object returned inside data[].

FieldTypeMeaning
auditLogIdstringStable identifier for one append-only audit event.
tenantIdstringTenant affected by the audited action.
modulestringModule responsible for the operation.
surfacestringSurface through which the action was initiated or exposed.
operationIdstringStable operation identifier, such as platform.tenants.create.
actorSubjectstringPseudonymous actor id for the user, service, or release process.
actorRolesarray<string>Roles or scopes that justified the action or explain the authorization failure.
resourceTypestringStable resource class affected by the action.
resourceIdstringIdentifier of the primary resource affected by the action.
actionstringBehavioral audit category from the audit_action enum.
outcomestringFinal result category from the audit_outcome enum.
httpStatusintegerHTTP status returned for the request or HTTP-equivalent maintenance result.
requestIdstringPer-request support correlation id.
traceIdstringTrace id linking logs, metrics, audit rows, and downstream spans.
idempotencyKeyIdstring or nullInternal retry-ledger row id when the audited operation used Idempotency-Key.
occurredAtstringUTC timestamp when the audited action reached the recorded outcome.
redactedMetadataobjectSafe structured context only: counts, hashes, version names, problem codes, and redacted summaries. Never raw bodies, tokens, contact data, IP addresses, user agents, or learner PII.

Problem Object

FieldTypeMeaning
typeURLStable dereferenceable Problem type URI.
codestringStable client branch key, such as tenant_scope_mismatch.
statusintegerHTTP status.
titlestringShort safe summary.
detailstringCaller-safe explanation; no secrets or learner PII.
requestId, traceIdstringSupport and log correlation.
fieldErrorsarrayOptional request-field issues for validation failures.

Module Registry

The registry is a live API view over loop-derived release state. Treat module_key as stable namespace identity, not proof that a surface is approved.

ModuleCurrent customer meaningPublished surfacesDocs
platform Shared tenant, auth, error, idempotency, audit, hosting, eval, and loop contracts. Its 1EdTech surface is under reconciliation while this cascade re-approves. 1edtech Architecture / Data dictionary
qti Assessments, questions, tests, packages, delivery sessions, answers, scoring, and trust evidence. 1edtech, alpha QTI 1EdTech / QTI Alpha
oneroster Rosters, classes, enrollments, academic sessions, users, and gradebook exchange. 1edtech OneRoster docs
caliper Learning activity events, analytics streams, and observational evidence across apps. 1edtech Caliper docs
case Competency and academic standards graphs, standards-package import/export, and external-ID alignment. 1edtech CASE docs
incept Governed generation-loop producer surface for Incept ledger provenance, quality evidence, repair, promotion, deployment, issue links, and progress projections. incept Producer surface / Data dictionary
Read live state when routing a consumer. The table above explains the expected rows, but GET /platform/modules is the customer-visible source for the current registry response.

Errors

Errors use typed RFC 7807 Problem Details with a canonical type URL, stable code, status, safe detail, requestId, traceId, and optional fieldErrors.

StatusCodeWhen it happensProblem docs
400invalid_requestBad body, missing tenantId=demo, invalid tenant slug, or missing required request field.invalid-request
401unauthorizedBearer token is missing, malformed, expired, or signed with the wrong secret.unauthorized
403forbiddenToken is valid but lacks the required role/scope, or public demo mint was attempted for a non-demo tenant.forbidden
403tenant_scope_mismatchTenant path and JWT tenant claim do not match, or a reviewer token attempts to read a demo handle.tenant-scope-mismatch
404not_foundNo matching route or no visible tenant/module/idempotency row.not-found
405method_not_allowedRoute exists but the HTTP method is not documented for it.method-not-allowed
409conflictIdempotency key reused with different content or tenant key uniqueness conflict.conflict
428precondition_requiredA required precondition header is missing, such as Idempotency-Key on a retryable write.precondition-required
500server_errorUnexpected platform or dependency failure.server-error
Example Problem
{
  "type": "https://platform3-andymontgomery-9773s-projects.vercel.app/problems/tenant-scope-mismatch",
  "code": "tenant_scope_mismatch",
  "status": 403,
  "title": "This token cannot access that tenant",
  "detail": "The path tenant does not match the token tenant claim.",
  "requestId": "req_...",
  "traceId": "trace_...",
  "fieldErrors": []
}

Provenance

This website is generated as the Platform 1EdTech customer website deliverable. It is intentionally narrow: every endpoint and field on this page comes from the approved Platform architecture, approved Platform data dictionary, and current implementation tests.

  • Benchmark studied: Stripe API reference. This page follows the same practical shape: base URL, auth, errors, endpoint contracts, request/response fields, status codes, and runnable examples on one page.
  • Architecture source: Platform 1EdTech Architecture, especially PITD-003, PITD-005, PITD-006, PITD-009, PITD-019, PITD-024, PITD-025, PITD-027, PITD-028, and PITD-031.
  • Dictionary source: Platform 1EdTech Data Dictionary, especially platform.tenant, platform.idempotency_key, platform.audit_log, module_key, module_release_status, tenant_status, audit_action, and audit_outcome.
  • Implementation source: loop/platform/artifacts/1edtech/implementation/impl, including service root, platform HTTP handler, module registry, tenant model, smoke tests, and Problem catalog.
  • Vendored context read: vendor/qti-spec-bundle/README.md and vendor/qti-spec-bundle/MANIFEST.md for self-contained 1EdTech reference policy; the Platform customer API endpoints themselves are not copied from QTI spec text.