October 2026
Custom fields added
Area | Call |
|---|---|
Courses |
|
Courses |
|
Learning paths |
|
Learning paths |
|
Learning paths |
|
ILT events |
|
VC events |
|
Skills |
|
Dashboard |
|
Dashboard |
|
GET /courses/{courseId} already returned custom fields. Its response is unchanged apart from the edge cases listed under "Other changes".
Notes added
Call | Change |
|---|---|
|
|
|
|
How custom fields are returned
The shape is the one
GET /courses/{courseId}already uses:"custom_fields": [{"name": "...", "value": "..."}]. This is not the/usersshape ([{"<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
notesis the "Notes" text shown for the user on the event's registrants page (the registrant'sassessment). 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 (commit53e60979), 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 wasnull.
API docs (app.schoox.com/api-docs): the Courses, Learning Paths, Dashboard, Events and Skills pages document
custom_fieldsandnotes.API docs renderer: response fields that allow more than one type (
oneOf) now show those types instead of none.custom_fields.valueshows string and number. The existingcertificate_expiration_periodfield (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 andGET /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
GetSkillCoursesTestnow 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).
/usersdoes the same. The exceptions areGET /courses?userId=andGET /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 outcustom_fieldswhen the academy has no course custom fields, and for public (non-academy) courses. The lists always include it.The
limitonGET /curriculumsis not capped, andGET /dashboard/curriculumsis not paginated.The courses nested inside
GET /curriculums/{curriculumId}(courses[]) don't carry custom fields.