# Canonical Caliper Surface

Use this file after the skill activates when you need surface details. Always prefer the hosted customer website and data dictionary if they differ from this file.

## Hosted sources

- Customer website: https://platform3-andymontgomery-9773s-projects.vercel.app/caliper/1edtech/customer_website/
- Data dictionary: https://platform3-andymontgomery-9773s-projects.vercel.app/caliper/1edtech/data_dictionary/
- Architecture: https://platform3-andymontgomery-9773s-projects.vercel.app/caliper/1edtech/architecture/
- Surface QC: https://platform3-andymontgomery-9773s-projects.vercel.app/caliper/1edtech/surface_qc/
- Live API base: https://platform3-andymontgomery-9773s-projects.vercel.app/caliper/1edtech/implementation/api

Official Caliper source bundle is pinned in platform3 under `vendor/caliper-prior-workspace/spec_bundle/MANIFEST.md`. The approved platform3 docs win over the older prior-workspace OpenAPI contract when they differ.

## Credentials

Demo tenant:

- Tenant id: `00000000-0000-4000-8000-00000000ca12`
- Demo mint: `POST $CALIPER_BASE_URL/dev/mint?tenantId=00000000-0000-4000-8000-00000000ca12`
- Demo sensor IRI returned by mint: `https://timeback.example.edu/sensors/caliper-demo`

Real or reviewer tenant:

- Use the operator-supplied bearer token, usually `CALIPER_REVIEWER_JWT` in platform3 loop contexts.
- Use the supplied tenant id or the JWT `tenantId` claim as `X-Timeback-Tenant`.
- Do not call `/dev/mint` for real tenants.

## Endpoints

| Operation | Method and path | Success | Required proof |
| --- | --- | --- | --- |
| Demo token mint | `POST /dev/mint?tenantId={tenantId}` | `200` JSON with token, tenantId, sensorIri | The tenant id is the public demo UUID. |
| Persist Caliper envelope | `POST /caliper/v1p2/events` | `204 No Content` | Capture `caliper-envelope-hash`; body is empty. |
| Read event projection | `GET /caliper/v1p2/events?eventIri={eventIri}` | `200` JSON | Use an event IRI from an envelope you posted or were handed. |
| Read envelope projection | `GET /caliper/v1p2/envelopes?hash={hash}` | `200` JSON | Use the server-returned envelope hash. |

Headers for writes and reads:

- `Authorization: Bearer <token>`
- `X-Timeback-Tenant: <tenant id>`
- `Content-Type: application/json` for POST only
- Optional `Idempotency-Key` for retry safety

## Accepted envelope shape

Use Caliper Analytics 1.2 names:

- Envelope requires `sensor`, `sendTime`, `dataVersion`, and non-empty `data`.
- `dataVersion` must be `http://purl.imsglobal.org/ctx/caliper/v1p2`.
- Event rows require `id`, `type`, `actor`, `action`, `object`, and `eventTime`.
- Use synthetic learner examples in public output. Do not echo real learner payloads or bearer tokens.

## Binary invariants

These are the minimum rules an app, report, or probe must preserve:

- `dataVersion` is exactly `http://purl.imsglobal.org/ctx/caliper/v1p2`.
- `X-Timeback-Tenant` and the bearer token tenant claim refer to the same tenant.
- Real-tenant sensor IRIs come from the operator or the surface; only the demo tenant uses the public demo sensor.
- `POST /caliper/v1p2/events` success is `204 No Content`, not `202` and not a JSON response body.
- Envelope hash proof comes from the `caliper-envelope-hash` response header.
- Reads use a supplied event IRI or a server-returned envelope hash. No collection discovery ships.
- Problem JSON responses are authoritative evidence of rejected input; do not replace them with local validation tables.

## Status and error semantics

- `200`: demo mint or read projection succeeded.
- `204`: Sensor API envelope accepted or safely deduplicated; no JSON body.
- `400`: malformed JSON, missing required fields, empty data, invalid query, or invalid demo tenant format.
- `401`: missing, expired, malformed, or wrongly signed bearer token.
- `403`: token/tenant mismatch, inactive or cross-tenant sensor, or forbidden demo mint tenant.
- `404`: no tenant-owned event or envelope for the supplied identifier.
- `409`: optional `Idempotency-Key` reused with different request content.
- `415`: POST content type is not JSON.
- `422`: parsed JSON violates Caliper dataVersion, event type, action, entity type, timestamp, profile, extension, or privacy validation.

Errors use Problem JSON with stable top-level `code`, `status`, `title`, `detail`, `requestId`, `traceId`, and `fieldErrors`.

## Deferred or unshipped behavior

Do not promise these:

- No per-event write model outside Caliper envelopes.
- No collection list endpoints.
- No filter, sort, cursor, or `modifiedSince` query model.
- No If-Match or mutable resource concurrency model.
- No outbound webhook, poll feed, or event stream.
- No public Caliper-specific DELETE or retention API.
- No official 1EdTech certification claim; the surface ships local evidence only.

When a user asks for one of these, report that the surface has not committed to it and link to the architecture decision axis in the hosted architecture page.
