# Worked Example

This example regenerates a Caliper activity receipt by surface calls only:

1. Mint a demo token.
2. POST a synthetic Caliper Analytics 1.2 `AssessmentItemEvent`.
3. Capture the server-returned envelope hash.
4. Read the event projection by event IRI.
5. Read the envelope projection by envelope hash.

It does not compute hashes, normalize profiles, query the database, or read implementation source.

## Demo workflow

```bash
export CALIPER_BASE_URL="${CALIPER_BASE_URL:-https://platform3-andymontgomery-9773s-projects.vercel.app/caliper/1edtech/implementation/api}"
export DEMO_TENANT_ID="00000000-0000-4000-8000-00000000ca12"

DEMO_TOKEN_RESPONSE=$(curl -fsS -X POST "$CALIPER_BASE_URL/dev/mint?tenantId=$DEMO_TENANT_ID")
export TOKEN=$(printf '%s' "$DEMO_TOKEN_RESPONSE" | jq -r '.token')
export TENANT=$(printf '%s' "$DEMO_TOKEN_RESPONSE" | jq -r '.tenantId')
export SENSOR_IRI=$(printf '%s' "$DEMO_TOKEN_RESPONSE" | jq -r '.sensorIri')

EVENT_UUID=$(uuidgen | tr '[:upper:]' '[:lower:]')
export EVENT_IRI="urn:uuid:$EVENT_UUID"
export IDEMPOTENCY_KEY="caliper-skill-pack-$EVENT_UUID"

REQUEST_BODY=$(mktemp)
RESPONSE_HEADERS=$(mktemp)

cat > "$REQUEST_BODY" <<EOF
{
  "sensor": "$SENSOR_IRI",
  "sendTime": "2026-06-02T21:55: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/skill-pack-learner",
        "type": "Person",
        "name": "Skill Pack Learner"
      },
      "action": "Completed",
      "object": {
        "id": "https://timeback.example.edu/items/skill-pack-proof",
        "type": "AssessmentItem",
        "name": "Skill pack proof item"
      },
      "generated": {
        "id": "https://timeback.example.edu/attempts/$EVENT_UUID",
        "type": "Attempt",
        "count": 1
      },
      "eventTime": "2026-06-02T21:54:48.000Z",
      "edApp": {
        "id": "https://timeback.example.edu/apps/timeback",
        "type": "SoftwareApplication",
        "name": "TimeBack"
      }
    }
  ]
}
EOF

STATUS=$(curl -fsS -o /dev/null -D "$RESPONSE_HEADERS" -w '%{http_code}' \
  -X POST "$CALIPER_BASE_URL/caliper/v1p2/events" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Timeback-Tenant: $TENANT" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
  --data-binary "@$REQUEST_BODY")

test "$STATUS" = "204"
export ENVELOPE_HASH=$(awk 'BEGIN{IGNORECASE=1} /^caliper-envelope-hash:/ {gsub(/\r/,"",$2); print $2}' "$RESPONSE_HEADERS" | head -1)

curl -fsS "$CALIPER_BASE_URL/caliper/v1p2/events?eventIri=$(printf '%s' "$EVENT_IRI" | jq -sRr @uri)" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Timeback-Tenant: $TENANT" | jq '{eventIri,eventType,profile,action,eventTime,eventHash}'

curl -fsS "$CALIPER_BASE_URL/caliper/v1p2/envelopes?hash=$(printf '%s' "$ENVELOPE_HASH" | jq -sRr @uri)" \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Timeback-Tenant: $TENANT" | jq '{envelopeHash,envelopeStatus,eventCount:(.events|length),events}'

rm -f "$REQUEST_BODY" "$RESPONSE_HEADERS"
```

Expected proof:

- POST status is `204`.
- `ENVELOPE_HASH` begins with `sha256:`.
- Event read returns `eventType: "AssessmentItemEvent"`, `profile: "AssessmentProfile"`, and `action: "Completed"`.
- Envelope read returns `envelopeStatus: "processed"` and `eventCount: 1`.

## Real or reviewer tenant variant

If the platform gives you `CALIPER_REVIEWER_JWT`, use the same `CALIPER_BASE_URL` and the tenant claim or supplied tenant id:

```bash
export TOKEN="${CALIPER_REVIEWER_JWT:?missing reviewer token}"
export TENANT="${CALIPER_TENANT_ID:-$(node -e 'const p=JSON.parse(Buffer.from(process.env.CALIPER_REVIEWER_JWT.split(".")[1],"base64url")); process.stdout.write(p.tenantId || p.tenant_id || "")')}"
export SENSOR_IRI="https://timeback.example.edu/sensors/caliper-reviewer"
```

Then run the same POST/read-back workflow. Never call `/dev/mint` for real tenants.
