Skip to end of banner
Go to start of banner

Web API Documentation for Customers

Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current Restore this Version View Version History

« Previous Version 8 Next »

This document contains detailed descriptions how to utilize the Web API from a customer's point of view.

=================================================================================================

It is important to understand the data structure of the LMS and how the objects connected to each other.

Basic LMS objects and their relation to each other:

  • The organization path (orgPath) /Root/[org url name] identifies your top level path (i.e. to get user profile data)
  • An organization contains navigation pages (NavPages) and courses (course paths). NavPages are used to structure/group courses and other content
  • Courses contain one or more course offerings.
  • A course offering contains student registrations and instructors
  • Every assessment result (i.e. exam) is bound to a specific student registration
  • Completing a course means completing a registration. If a user retakes a course, they get re-registered and will complete a new registration

Please note that some parameters are optional and may be useful to you to filter the data you need.

The 'path' or 'course path' is an internal LMS path to identify the page or course you would like to get information from. Every path starts with /Root/[org url name] which identifies your organization in the LMS. If you navigate to a page/course in the LMS you can find the current path you are at in the browser Url. For example, the LMS path for https://orgname.marinels.com/Cnt/Root/OrgUrlName/PedestalCraneOpsManual/ is /Root/OrgUrlName/PedestalCraneOpsManual.

A list of courses and their paths can be retrieved via API endpoint Services/api/Content/GetCoursePaths?path={path}. The parameter 'path' could be that of the main organization (/Root/[org url name]) or a sub path to only get a subset of courses in your system.

Every course has at least one course offering, identified by an Id (GUID). The course offerings themselves reference the student registration objects. In order to get the course offerings of a course, you can use either API endpoint Services/api/Registration/GetCourseOfferingsByPath?coursePath={coursePath}&pageSize={pageSize}&startIndex={startIndex}&sortField={sortField}&sortDirection={sortDirection}&startDate={startDate}&endDate={endDate}&nameFilter={nameFilter} or Services/api/Registration/GetDefaultOfferingByCoursePath?coursePath={coursePath} using a coursePath from the previous call 'GetCoursePaths'. 
Most of our customers only use 1 course offering per course. If that is true for you the endpoint Services/api/Registration/GetDefaultOfferingByCoursePath?coursePath={coursePath} may be useful to get the one course offering of a course.

Once you have identified the course offerings you are interested in you can get student registrations bound to those course offerings via API endpoint Services/api/Registration/GetRegistrationsByCourseOfferingId?coursePath={coursePath}&offeringId={offeringId}&pageSize={pageSize}&startIndex={startIndex}&fromRegistrationDate={fromRegistrationDate}&toRegistrationDate={toRegistrationDate}.

FYI: We do have 2 additional API calls which return active or completed registrations across all courses or only for a subset of courses in an upcoming release (version 3.9.4).

A course offering may have instructors associated - the API call Services/api/Registration/GetInstructorsByCourseOfferingId?offeringId={offeringId} would get you the instructors of a specific course offering.

A registration may contain assessment data like exam results. To retrieve those, API endpoints Services/api/Audit/GetStudentResultsByRegistrationId?registrationId={registrationId} and Services/api/Audit/GetStudentResultsByRegistrationIds can be used, passing the registration Ids from previous API calls.

User information is returned where applicable (i.e. registration). If user information is returned as part of an object like registration only a so called 'shallow user' is returned. A shallow user object contains minimal information to identify the user. Additional API endpoints are available to get full user profile information. Use the Id (GUID) of the shallow user to retrieve a full user profile via Services/api/User/GetUser?userId={userId}, Services/api/User/GetUsersByIds?path={path} or Services/api/User/GetUsersWithOrgProfileValues?path={path} just to list a few endpoints.

Please refer to https://orgname.marinels.com/help section 'User' for all user related API endpoints.

In addition to standard user information like first name, last name, email address optional 'OrgProfileFields' (OPFs) are available and are configured on the organization. If OPFs are configured the user values for OPFs can be retrieved via endpoints 

Services/api/User/GetOrgProfileValuesForUser?userId={userId} 
Services/api/User/GetUsersWithOrgProfileValues?path={path} 
Services/api/User/GetOrgProfileValuesForCurrentUser
Please refer to https://orgname.marinels.com/help section 'User' for all user related API endpoints. 

API Interface Documentation

All available API endpoints (except the login endpoint) can be found on your LMS url + /help (no login is required). 

For example https://lms.marinels.com/help. It will redirect you to the API documentation https://lms.marinels.com/swagger/ui/index 


This document only contains a selection of endpoints with detailed information on how to use them.

API Connection Protocol

Please note that all connections to the API are required to use the security protocol TLS v1.2.

Example in C#

System.Net.ServicePointManager.SecurityProtocol = System.Net.ServicePointManager.SecurityProtocol | System.Net.SecurityProtocolType.Tls12;


Logon (Post)

POST https://[fill-in-your-lms-domain]/services/api/oauth2/token

Body Parameters

grant_type: ‘password’

username:  org login Id or LMS user name

password: login password as clear text

scope: path to the organization like /Root/MyOrg (mandatory if org login Id is used for username)

Headers

Content-Type: application/x-www-form-urlencoded


Response

access_token is used to call into the API endpoints for authentication

refresh_token is used to refresh the access_token and extend the expiry of the token

expires_in in seconds until the access_token and refresh_token is invalid


Refresh Token

POST https://[fill-in-your-lms-domain]/services/api/oauth2/token

Body Parameters

grant_type: refresh_token

refresh_token: Use the refresh_token from the response during login.

Headers

Content-Type: application/x-www-form-urlencoded

Response

access_token is used to call into the API endpoints for authentication

refresh_token is used to refresh the access_token and extend the expiry of the token

expires_in in seconds until the access_token and refresh_token is invalid

JSON Result Property Mapping

Some JSON result properties have integer values with a specific meaning. This section lists all mappings.


  • RegistrationStatus 
    • 0 = Pending
    • 1 = Active
    • 2 = Completed
  • RegistrationCreationType 
    • 0 = Unknown
    • 1 = Historical
    • 2 = Auto
    • 3 = Imported
    • 4 = Self
    • 5 = Manual
  • StudentResultAuditType 
    • 0 = ExamInstance
    • 1 = EvaluationInstance
    • 2 = ExternalGradeInstance

GetActiveRegistrationData

POST 

https://https://[fill-in-your-lms-domain]/Services/api/ExternalRegistration/GetActiveRegistrationData


URL Parameters

orgPath={orgPath}

fromDate={from date and time of format ISO 8601 in UTC (i.e. yyyy-mm-ddThh:mmZ)}

suppressCourseCodeFiltering={suppressCourseCodeFiltering}


Notes: 

  • The fromDate is the fence post date and time to return registrations with modification date > fromDate
  • ‘suppressCourseCodeFiltering’ is optional and is set to ‘false’ by default. ‘false’ retains the existing behaviour.

Headers

Content-Type: application/json

Authorization: Bearer [access_token from the login]

Accept: application/json


Notes:

Alternative values for Content-Type and Accept: application/xml 

Body

Optional but recommended paging via JSON object 

Response

The response has a structure with the following fields:

  • Items: array of registrations. Empty [ ], if no registrations were found. Max. PageSize registrations are returned for one request. All registrations are returned if no page size has been passed in the request (not recommended)
  • TotalItems: count of all registrations found according to the request parameters
  • PageSize: page size passed in the request. 0 if no page size has been requested
  • FirstIndexOfNextPage: use this value as StartIndex to retrieve the next page. If FirstIndexOfNextPage >= TotalItems you have reached the last page. Continue to get the next page as long as FirstIndexOfNextPage < TotalItems.
    Always 0 if no pageInfo or PageSize was set for the request.

Response Data:

{

"Items": [

{

"CourseCode": "9973469",

"FirstName": "Pat327",

"LastName": "Learner",

"OrganizationLoginId": "PatLearner327",

"UniqueLoginId": "PatLearner327",

"EmailAddress": "PatLearner327@marinels.com",

"ContactEmail": "PatLearner327@marinels.com",

"CourseName": "OSHA 1",

"CourseId": "2806f1bb-2eb5-11e7-80c0-0cc47a6f32d1",

"OfferingShortId": "OSHA1",

"RegistrationDate": "2017-07-07T22:13:49.3670000Z",

"RegistrationId": "bb06f1bb-2eb5-11e7-80c0-0cc47a6f32cb",

"RegistrationStatus": 1,

"OrgProfileFieldValues": [

{

"Name": "Rank",

"Value": "none"

},

{

"Name": "Vessel",

"Value": "My Vessel 1"

}

]

},

{

"CourseCode": "9973469",

"FirstName": "Pat326",

"LastName": "Learner",

"OrganizationLoginId": "PatLearner326",

"UniqueLoginId": "PatLearner326",

"EmailAddress": "PatLearner326@marinels.com",

"ContactEmail": "PatLearner326@marinels.com",

"CourseName": "OSHA 1",

"CourseId": "2806f1bb-2eb5-11e7-80c0-0cc47a6f32d1",

"OfferingShortId": "OSHA1",

"RegistrationDate": "2017-07-07T22:13:49.5370000Z",

"RegistrationId": "c806f1bb-2eb5-11e7-80c0-0cc47a6f32ff",

"RegistrationStatus": 1,

"OrgProfileFieldValues": [

{

"Name": "Rank",

"Value": "none"

},

{

"Name": "Vessel",

"Value": "My Vessel 2"

}

]

}

],

"TotalItems": 7502,

"PageSize": 2,

"FirstIndexOfNextPage": 2

}


GetCompletedRegistrationData

POST https://https://[fill-in-your-lms-domain]/Services/api/ExternalRegistration/GetCompletedRegistrationData

URL Parameters

orgPath={orgPath}

fromDate={from date and time of format ISO 8601 in UTC (i.e. yyyy-mm-ddThh:mmZ)}

suppressCourseCodeFiltering={suppressCourseCodeFiltering}


Notes: 

  • The fromDate is the fence post date and time to return registrations with modification date > fromDate
  • ‘suppressCourseCodeFiltering’ is optional and is set to ‘false’ by default. ‘false’ retains the existing behaviour.

Headers

Content-Type: application/json

Authorization: Bearer [access_token from the login]

Accept: application/json


Notes:

Alternative values for Content-Type and Accept: application/xml 

Body

Optional but recommended paging via JSON object 

Response

The response has a structure with the following fields:

  • Items: array of registrations. Empty [ ], if no registrations were found. Max. PageSize registrations are returned for one request. All registrations are returned if no page size has been passed in the request (not recommended)
  • TotalItems: count of all registrations found according to the request parameters
  • PageSize: page size passed in the request. 0 if no page size has been requested
  • FirstIndexOfNextPage: use this value as StartIndex to retrieve the next page. If FirstIndexOfNextPage >= TotalItems you have reached the last page. Continue to get the next page as long as FirstIndexOfNextPage < TotalItems.
    Always 0 if no pageInfo or PageSize was set for the request.

Response Data:


Which Registrations are Included in the Response?

The registration completion date and fromDate parameter usually correlate with each other. However, if the data is coming from a vessel via sync the completion date and the time when it would show up in a GetCompletedRegistrationData call does not correlate with the passed in fromDate. The completion date of a registration is the time an exam/evaluation was completed to reflect the completion as accurately as possible (e.g. on a vessel). However, the synchronization of data from Vessel to Main and registration completion rules may take a bit of time (depending on sync interval and connectivity of the vessel and when registration processing happens on Main). If the API were only checking for completion date >= fromDate it would miss completed registrations which were synced after the fromDate but had completion dates before the fromDate - due to the sync/processing delay the completion date would be in the past. Therefore, the fromDate checks the ModifiedDate of the registration to determine whether it should get exported. That is to ensure you don't miss completed registrations which have been completed in the past but synced at a later date because the vessel was not able to sync the data in time.

Also, to avoid missing any registrations due to processing delays on the Main system we automatically reduce the fromDate parameter by 5 min. to ensure we include any completed registration which may have been in processing during the last API call.


Example:

  • CompletionDate: 2022-07-14 21:41:44.963
  • DateModified: 2022-07-14 22:58:21.073

The DateModified is the date the registration (and exam data) has been synched and processed from Vessel to the Main system. The fromDate (-5min.) is compared to the DateModified and not against the CompletionDate.


Using a fromDate 2022-07-14T23:03:30.000Z would not include this record because 2022-07-14T23:03:30.000Z minus 5 min. = 2022-07-14T22:58:30.000. The DateModified of the record is 2022-07-14 22:58:21.073 and therefore about 9sec. BEFORE the fromDate. That is why it would not include the record using a fromDate = 2022-07-14T23:03:30.000Z.


Using a fromDate 2022-07-14T23:03:10.000Z would include the registration record: fromDate = 2022-07-14T23:03:10.000Z minus 5 min. = 2022-07-14T22:58:10.000. The DateModified of the record is 2022-07-14 22:58:21.073 and therefore AFTER the fromDate. That is why it would include the record using a fromDate = 2022-07-14T23:03:10.000Z.


A side note to be aware of for selecting the next fromDate:

Calculating the fromDate for the next interval should be the date and time just before you issue the first GetCompletedRegistrationData call. This ensures that you don't miss out any registration records due to the time the API calls and processing of the retrieved data takes.

  1. currentFromDate = retrieve stored date and time from last time
  2. nextFromDate = Now()
  3. call GetCompletedRegistrationData(currentFromDate,...) and data processing
  4. On success: store nextFromDate to load next time as currentFromDate

Paging API Calls

Most API endpoints support paging if the expected response can be large. The paging info is passed as request body (also see details for API endpoints) and is always recommended if available.


Request body:

{"PageSize":60000, "StartIndex":0}



Response object properties:

  • Items: array of registrations. Empty [ ], if no registrations were found. Max. PageSize registrations are returned for one request. All registrations are returned if no PageSize has been passed in the request (not recommended)
  • TotalItems: count of all registrations found according to the request parameters
  • PageSize: page size passed in the request. 0 if no PageSize has been requested (not recommended)
  • FirstIndexOfNextPage: use this value as StartIndex in your request to retrieve the next page. If FirstIndexOfNextPage >= TotalItems you have reached the last page. In other words, continue to get the next page as long as FirstIndexOfNextPage < TotalItems.

Note: FirstIndexOfNextPage would always be 0 if no pageInfo or PageSize was set for the request (not recommended).


Attention on Changing TotalItems

{

"Items": [

{

"CourseCode": "TEST",

"FirstName": "Pat222",

"LastName": "Learner",

"OrganizationLoginId": "PatLearner222",

"UniqueLoginId": "PatLearner222",

"EmailAddress": "PatLearner222@marinels.com",

"ContactEmail": "PatLearner222@marinels.com",

"CourseName": "Initiation A",

"CourseId": "a5d2a4aa-19f1-11e7-80bf-0cc47a6f32d0",

"OfferingShortId": "InitA",

"CompletionDate": "2017-04-07T23:14:48.9370000Z",

"RegistrationDate": "2017-04-05T18:18:28.7070000Z",

"RegistrationId": "aa06f1bb-2eb5-11e7-80c0-0cc47a6f32c8",

"RegistrationStatus": 2,

"OrgProfileFieldValues": [

{

"Name": "Rank",

"Value": "none"

},

{

"Name": "Vessel",

"Value": "My Vessel 1"

}

]

},

{

"CourseCode": "TEST",

"FirstName": "Pat143",

"LastName": "Learner",

"OrganizationLoginId": "PatLearner143",

"UniqueLoginId": "PatLearner143",

"EmailAddress": "PatLearner143@marinels.com",

"ContactEmail": "PatLearner143@marinels.com",

"CourseName": "Initiation A",

"CourseId": "a5d2a4aa-19f1-11e7-80bf-0cc47a6f32d0",

"OfferingShortId": "InitA",

"CompletionDate": "2017-07-02T09:48:56.3370000Z",

"RegistrationDate": "2017-04-06T21:07:45.1530000Z",

"RegistrationId": "ee06f1bb-2eb5-11e7-80c0-0cc47a6f32c1",

"RegistrationStatus": 2,

"OrgProfileFieldValues": [

{

"Name": "Rank",

"Value": "none"

},

{

"Name": "Vessel",

"Value": "My Vessel 1"

}

]

}

],

"TotalItems": 98,

"PageSize": 2,

"FirstIndexOfNextPage": 2

}

The API calls are operating in a dynamic and busy environment across multiple transactions. That is, parallel processes may modify data you may request. Therefore, the result count returned may change while paging through the result set via multiple API calls. That means, more or less items are returned than the first TotalItems may have expressed - keeping the TotalItems close to the actual state of the system. Having said that, using the fromDate guarantees that you get all required items (i.e. completed registrations). That is, no registrations are omitted due to the changing environment. If you don’t get the registration for this round of API calls you will get them the next time.

For example, you get all active registrations - during your processing, an active registration is completed. While retrieving the first page of active registrations, you get a value for TotalItems. Because an active registration was completed in the meantime, the next call to retrieve active registrations will have TotalItems= TotalItems-1. The same is true when getting completed registrations - the TotalItems will be TotalItems+1 due to the fact that a new completed registration came into the fold. That is natural in a live environment. The property FirstIndexOfNextPage will accommodate for that or you may get the completed registration the next time you retrieve completed registrations.

It is important to call the API while FirstIndexOfNextPage < TotalItems

You must not rely on the first TotalItem count and expect exactly the number of items from subsequent calls.