API Conventions¶
Everything on this page applies across the whole API — learn it once, use it everywhere.
Response Format (JSON-LD / Hydra)¶
Responses use JSON-LD with the Hydra vocabulary. Single resources carry @context, @id, and @type alongside their data fields; collections wrap items in an envelope:
{
"@context": "/api/partner/v1/contexts/Course",
"@id": "/api/partner/v1/courses",
"@type": "hydra:Collection",
"hydra:member": [ { "…": "resource objects" } ],
"hydra:totalItems": 109,
"hydra:view": {
"@id": "/api/partner/v1/courses?page=1",
"hydra:first": "/api/partner/v1/courses?page=1",
"hydra:last": "/api/partner/v1/courses?page=22",
"hydra:next": "/api/partner/v1/courses?page=2"
}
}
Treat the @-prefixed and hydra: keys as metadata: read your data from hydra:member (collections) or the top-level fields (single resources).
Field Naming¶
Resource fields are camelCase: firstName, organizationId, passingScore, createdAt, updatedAt.
One exception
The two content-delivery endpoints (/modules/{id}/content and /modules/{id}/refresh-url) return snake_case fields (content_url, expires_at, module_id). Match each endpoint's documented example rather than assuming one convention globally.
Request Content Types¶
| Operation | Content-Type |
|---|---|
POST with a body |
application/json |
PATCH |
application/merge-patch+json — required; a PATCH with application/json is rejected |
GET / DELETE |
(no body) |
Merge-patch semantics: send only the fields you want to change; set a field to null to clear it.
The X-User-ID Header¶
Catalog endpoints are partner-scoped: the JWT alone identifies you. User-scoped endpoints additionally need to know which learner the call is about — pass their UUID:
Requires X-User-ID |
Doesn't |
|---|---|
GET /modules/{id}/content |
GET /ping |
GET /modules/{id}/refresh-url |
POST /users/register |
POST /modules/{id}/complete |
GET /users/{uuid} (UUID is in the path) |
GET /course-progresses (+/{id}) |
GET /organizations, /categories, /courses, /modules (+ details) |
GET /transcripts (+/{id}) |
|
GET /transcript/pdf, GET /transcripts/{id}/certificate/pdf |
The UUID must belong to a learner you registered. A UUID you don't own behaves exactly like one that doesn't exist (404) — partner isolation is absolute.
Pagination¶
Collections are paginated with query parameters:
page— 1-based page numberitemsPerPage— page size- Walk
hydra:view.hydra:nextuntil it's absent to iterate a full collection.
Delta Sync (updatedAt Filters)¶
Five collections — /categories, /courses, /modules, /course-progresses, /transcripts — support filtering on updatedAt, so you fetch only what changed since your last sync instead of re-pulling everything:
| Filter | Matches records where updatedAt is… |
|---|---|
updatedAt[after] |
≥ the given ISO 8601 timestamp |
updatedAt[before] |
≤ the timestamp |
updatedAt[strictly_after] |
> the timestamp |
updatedAt[strictly_before] |
< the timestamp |
Recommended pattern: store the timestamp you started each sync at, and use it as updatedAt[after] next time.
Note
Domain-event fields (lastActivityAt on progress, completionDate on transcripts) are not the sync cursor — always cursor on updatedAt.