API Additions — 2026

October 2026

Custom fields added

Area

Call

Courses

GET /courses, including search and multi-term search=a,b

Courses

GET /courses/user/{userId} and GET /courses?userId=

Learning paths

GET /curriculums, including search

Learning paths

GET /curriculums/{curriculumId}

Learning paths

GET /curriculums/user/{userId}

ILT events

GET /events/ilt, GET /events/ilt/{eventId}, GET /events/ilt/{eventId}/offers

VC events

GET /events/vc, GET /events/vc/{eventId}, GET /events/vc/{eventId}/offers

Skills

GET /skills/{skillId}/courses

Dashboard

GET /dashboard/users/{userId}/courses, GET /dashboard/users/{userId}/curriculums

Dashboard

GET /dashboard/courses (every status, and list=true), GET /dashboard/curriculums

 
GET /courses/{courseId} already returned custom fields. Its response is unchanged apart from the edge cases listed under "Other changes".

 

Notes added

Call

Change

GET /events/ilt/{eventId}/usersAttendanceData

notes for each user

GET /events/vc/{eventId}/usersAttendanceData

notes for each user, plus the behaviour change below

 

How custom fields are returned

  • The shape is the one GET /courses/{courseId} already uses: "custom_fields": [{"name": "...", "value": "..."}]. This is not the /users shape ([{"<name>": value}]), so existing course-details integrations keep working.

  • Every custom field the academy defines for that type is listed, in the order the fields were created. A field with no value has "".

  • Dropdown: the selected option's label. A stored value that matches no option (for example, a deleted option) gives "".

  • Number: a JSON number. A number too large to encode (for example 1e999) gives "".

  • Text, date and date-time: the stored value, as a string.

  • An academy with no custom fields for the type returns "custom_fields": [] on list items.

Notes

  • notes is the "Notes" text shown for the user on the event's registrants page (the registrant's assessment). It is "" when there are none.

  • Notes are returned as they were typed. The VC save stores a backslash before each quote (didn\'t), so the call removes it. ILT notes are stored without them.

Behaviour change: GET /events/vc/{eventId}/usersAttendanceData

  • The call now returns the users who completed the event, as the ILT call does.

  • Before this change it returned only registrants not yet synced to the VC provider (client_data = 'Pending'). Since 2022 (commit 53e60979), the "completed only" flag had been passed in the wrong argument position.

    • On events synced to the provider, it left out the real attendees and their notes.

    • On events not synced, it returned every registrant, attended or not.

  • ILT is unchanged: it lists the users who completed the event, so absent registrants and their notes are not listed.

Other changes

  • GET /courses/{courseId}: custom fields now come from the same shared service as the lists. The output is unchanged except in these edge cases:

    • A dropdown value that matches no option now gives "". Before, it gave another option's label when the stored text was a number.

    • A number too large to encode now gives "". Before, it made the whole response fail to encode.

    • A value stored twice for the same course is now listed once. Before, it appeared twice.

    • A field with no name now has name "". Before, it was null.

  • API docs (app.schoox.com/api-docs): the Courses, Learning Paths, Dashboard, Events and Skills pages document custom_fields and notes.

  • API docs renderer: response fields that allow more than one type (oneOf) now show those types instead of none. custom_fields.value shows string and number. The existing certificate_expiration_period field (Courses, Learning Paths, Events) now shows string and integer.

  • Performance: each call loads the values in batches of 500 ids, with 2–3 queries per batch and no per-item queries.

  • Code: a new CustomFieldApiValueService (app/Schoox/Academy/CustomFields/Services/Api/) serves every call above and GET /courses/{courseId}.

  • Tests:

    • 31 unit tests for the service and 4 for the docs renderer.

    • 26 API V1 tests across courses, learning paths, events (custom fields and notes), skills and dashboard.

    • The existing GetSkillCoursesTest now allows the new key.

Known limitations (not changed here)

  • Field names and dropdown labels use the language of the API key's user (the academy superadmin). /users does the same. The exceptions are GET /courses?userId= and GET /courses/user/{userId}, which use the target user's language.

  • A dropdown option translated into that language can come back as "". This already happens on /users.

  • GET /courses/{courseId} leaves out custom_fields when the academy has no course custom fields, and for public (non-academy) courses. The lists always include it.

  • The limit on GET /curriculums is not capped, and GET /dashboard/curriculums is not paginated.

  • The courses nested inside GET /curriculums/{curriculumId} (courses[]) don't carry custom fields.

Was this article helpful?
0 out of 0 found this helpful