Demo setup
Use the shared implementation deploy for unauthenticated demo token minting. The seeded demo tenant is the platform tenant UUID 00000000-0000-4000-8000-00000000ca12.
export CALIPER_BASE_URL="https://platform3-andymontgomery-9773s-projects.vercel.app/caliper/1edtech/implementation/api"
export BASE="$CALIPER_BASE_URL"
export DEMO_TENANT_ID="00000000-0000-4000-8000-00000000ca12"
DEMO_TOKEN_RESPONSE=$(curl -fsS -X POST "$BASE/dev/mint?tenantId=$DEMO_TENANT_ID")
export TOKEN=$(node -e 'const r=JSON.parse(process.argv[1]); process.stdout.write(r.token)' "$DEMO_TOKEN_RESPONSE")
export TENANT=$(node -e 'const r=JSON.parse(process.argv[1]); process.stdout.write(r.tenantId)' "$DEMO_TOKEN_RESPONSE")
export SENSOR_IRI=$(node -e 'const r=JSON.parse(process.argv[1]); process.stdout.write(r.sensorIri || "https://timeback.example.edu/sensors/caliper-demo")' "$DEMO_TOKEN_RESPONSE")
Real-tenant setup
Real-tenant tokens are minted out of band. The loop driver writes CALIPER_BASE_URL and CALIPER_REVIEWER_JWT to .env.local; the tenant header comes from the token claim.
set -a
source .env.local
set +a
export BASE="${CALIPER_BASE_URL:?driver has not written the canonical Caliper implementation URL}"
export TOKEN="${CALIPER_REVIEWER_JWT:?driver has not minted the Caliper reviewer JWT}"
export TENANT=$(node -e 'const token=process.env.CALIPER_REVIEWER_JWT || ""; const part=token.split(".")[1]; if (!part) process.exit(1); process.stdout.write(JSON.parse(Buffer.from(part, "base64url")).tenantId)')
Create the envelope
This sample uses Caliper's official names: sensor, sendTime, dataVersion, Event id/type/actor/action/object/eventTime, and standard entity classes. The example data is synthetic.
export EVENT_IRI="urn:uuid:$(node -e 'process.stdout.write(crypto.randomUUID())')"
cat > caliper-envelope.json <<JSON
{
"sensor": "$SENSOR_IRI",
"sendTime": "2026-05-24T15:00:00.000Z",
"dataVersion": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"data": [
{
"@context": "http://purl.imsglobal.org/ctx/caliper/v1p2",
"id": "$EVENT_IRI",
"type": "AssessmentItemEvent",
"actor": {
"id": "https://timeback.example.edu/users/student-1",
"type": "Person",
"name": "Ada Learner"
},
"action": "Completed",
"object": {
"id": "https://timeback.example.edu/items/fractions-1",
"type": "AssessmentItem",
"name": "Fractions check"
},
"generated": {
"id": "https://timeback.example.edu/attempts/attempt-1",
"type": "Attempt",
"count": 1,
"assignable": {
"id": "https://timeback.example.edu/assignments/fractions",
"type": "AssignableDigitalResource",
"name": "Fractions practice"
}
},
"eventTime": "2026-05-24T14:59:42.000Z",
"edApp": {
"id": "https://timeback.example.edu/apps/timeback",
"type": "SoftwareApplication",
"name": "TimeBack"
},
"group": {
"id": "https://timeback.example.edu/classes/math-3-a",
"type": "CourseSection",
"name": "Math 3A"
},
"membership": {
"id": "https://timeback.example.edu/memberships/student-1-math-3-a",
"type": "Membership",
"roles": [
"Learner"
],
"status": "Active"
},
"extensions": {
"https://timeback.example.edu/extensions/outcomeScore": 1
}
}
]
}
JSON
cat caliper-envelope.json
Post it
A successful request produces no body. Retry with the same payload is safe because Caliper canonical hashes deduplicate the envelope and event.
export POST_HEADERS=$(mktemp)
export POST_BODY=$(mktemp)
export IDEMPOTENCY_KEY="caliper-demo-$(date +%s)-$(node -e 'process.stdout.write(crypto.randomUUID())')"
curl -fsS -D "$POST_HEADERS" -o "$POST_BODY" -X POST "$BASE/caliper/v1p2/events" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Timeback-Tenant: $TENANT" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
--data @caliper-envelope.json
export ENVELOPE_HASH=$(grep -i '^caliper-envelope-hash:' "$POST_HEADERS" | head -1 | cut -d' ' -f2 | tr -d '\r')
test -n "$ENVELOPE_HASH"
HTTP/2 204
Verify the event
Read projection endpoints are TimeBack operational APIs for demo, QC, and integration proof. They are not official Caliper Sensor API certification operations.
curl -fsS -G "$BASE/caliper/v1p2/events" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Timeback-Tenant: $TENANT" \
--data-urlencode "eventIri=$EVENT_IRI"
Verify the envelope
The write response exposes the canonical envelope hash in the caliper-envelope-hash header. Use that hash to inspect the envelope projection without reading source code or database rows.
curl -fsS -G "$BASE/caliper/v1p2/envelopes" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Timeback-Tenant: $TENANT" \
--data-urlencode "hash=$ENVELOPE_HASH"