Skip to main content

API Contracts

:::info Source Sourced from services/progress-service/API_CONTRACTS.md in the documentation repo. :::

1. Surfaces

  • Internal REST: /api/v1/… (authenticated, tenant-scoped).
  • xAPI: /xapi/statements and related (xAPI 1.0.3 compliant).
  • Inter-service: NATS subscription to delivery-service + assessment-service events.

2. Versioning

  • /api/v1/… for internal; /xapi/ is spec-defined (xAPI 1.0.3, frozen).
  • Minor changes via X-API-Version response header.

3. Authentication

  • Authorization: Bearer <jwt> (internal).
  • X-API-Key for xAPI integrations (per-tenant keys).
  • X-Tenant-Id header required; must match JWT tid.

4. Endpoints

4.1 xAPI (LRS)

POST /xapi/statements
Body: Statement | Statement[]
Returns: 200 OK with { statementIds: [...] }

GET /xapi/statements/{id}
Returns: Statement

GET /xapi/statements?agent=...&verb=...&activity=...&since=...&until=...&limit=100&ascending=true
Returns: { statements: [...], more: "/xapi/statements?cursor=..." }

HEAD /xapi/statements?... (conformance)

PUT /xapi/activities/state (state API)
GET /xapi/activities/state
DELETE /xapi/activities/state

PUT /xapi/activities/profile (profile API)
GET /xapi/activities/profile

GET /xapi/about (returns supported versions)

4.2 Internal

GET /api/v1/attempts/{id}
GET /api/v1/attempts?filter[enrollmentId]=...&filter[outcome]=passed&page[size]=50
POST /api/v1/attempts/{id}/close (admin-only; emergency close)

GET /api/v1/enrollments/{eid}/progress
Returns: {
enrollmentId, courseId, courseVersionId, userId,
attempts: [ { id, number, state, outcome, score, startedAt, endedAt } ],
latestAttempt: { ... },
overallState: 'not_started' | 'in_progress' | 'completed' | 'failed',
completion: null | { completedAt, score, certificateId? }
}

GET /api/v1/enrollments/{eid}/transcript?format=json|pdf
Returns: full transcript including interactions, scores, completion proof.

GET /api/v1/users/{uid}/transcripts
GET /api/v1/users/{uid}/statements (paginated)

5. Request/Response Schemas

Statement (xAPI 1.0.3)

{
"id": "01H...",
"actor": { "account": { "homePage": "https://id.ghasi.io", "name": "user_ulid" } },
"verb": { "id": "http://adlnet.gov/expapi/verbs/passed", "display": { "en-US": "passed" } },
"object": { "id": "urn:ghasi:course_version:cv_01H...", "definition": { "name": { "en-US": "Course Title" } } },
"result": { "score": { "scaled": 0.87 }, "success": true, "completion": true },
"context": { "registration": "reg_ulid", "contextActivities": {...} },
"timestamp": "2026-04-15T09:23:00Z",
"stored": "2026-04-15T09:23:02Z",
"authority": { "account": { "homePage": "https://id.ghasi.io", "name": "delivery-service" } }
}

Progress Response

Provided in §4.2 above.

6. Error Model (problem+json)

Common codes:

  • validation.statement.invalid — fails xAPI schema (422)
  • validation.enrollment.not_found — statement has no matching enrollment (422)
  • validation.tenant.mismatch — actor/tenant mismatch (403)
  • resource.not_found — attempt / statement / enrollment absent (404)
  • cursor.stale — cursor filters changed (410)
  • rate.limited — ingestion rate exceeded (429)

7. Pagination

  • limit (xAPI) capped at 100; internal cursor paginated (page[size] ≤ 200).
  • Cursor payload includes storedBefore, storedAfter, filters fingerprint.

8. Rate Limits

EndpointLimitPer
POST /xapi/statements10k/sectenant
POST /xapi/statements (batch)100 MB/mintenant
GET /xapi/statements100/minuser
GET /api/v1/enrollments/{eid}/progress600/minuser

9. Security

  • xAPI endpoints require xapi:read / xapi:write scope.
  • Tenant isolation: agent filter narrowed to caller's tenant.
  • API keys logged by prefix only.
  • Statements containing PII outside actor.account.name redacted on log.

10. Idempotency

  • xAPI statements: statementId PK; duplicates silently accepted (spec requirement).
  • Internal writes use Idempotency-Key.

11. Performance Targets

  • Ingestion: 10k statements/sec/region sustained.
  • Query p95 < 300ms.
  • Transcript render p95 < 2s.

12. Monorepo contract hub (@ghasi/contracts)

  • OpenAPI (aggregated): Ghasi-edTech/packages/contracts/openapi/progress-service.openapi.json — keep in sync with services/progress-service/openapi.json via pnpm --filter @ghasi/contracts sync:openapi in the implementation monorepo.
  • AsyncAPI (machine-readable events): Ghasi-edTech/packages/contracts/asyncapi/progress-service.asyncapi.yaml — run pnpm --filter @ghasi/contracts validate in CI. This document remains the normative source for review, versioning, and outbox policy; extend the AsyncAPI channel list when you add published subjects.