Overview 

Restful API v1.5 is a significant upgrade from v1, which is being sunset on March 31, 2023. While new clients can onboard directly to the new version, v1 clients must upgrade to take advantage of new features and capabilities.

For more information on new v1.5 features, see Appendix A.
For more details on why the upgrade and sunset are necessary, see Appendix B.

To make upgrading easier, we have:

  1. Incorporated a backward compatibility setting that minimizes client code changes.
  2. Created this comprehensive guide to take you through the entire upgrade process.
  3. Trained and equipped your dedicated Client Success Manager, Account Manager, and Absorb Support team, so they are ready to answer your questions and provide support.

Upgrade Steps

Here are the steps we suggest Absorb LMS Admins take to initiate and complete the upgrade process.

 

Step One: Plan Out the Changes

Gather the technical team and explain the need and timing of the upgrade. To evaluate the effort required for your organization, first identify how the API is used in your system:

  1. Find every place in your code that calls the API.
    • Every API call will require changes to the service path and https header.

    • See Step Two for more details on the required changes.

  2. Identify where paginated endpoints are called. 
    • In addition to the changes outlined in the previous step, paginated endpoints will require special treatment. Instead of a single GET, multiple calls will be required to access all items.

    • Also, consider if filtering and/or sorting will reduce the need to GET all resources. When used correctly, these optional parameters can result in a more targeted data set, returned in optimal order for your operation.

    • See Step Two for more details on using filtering and sorting.

      List of paginated endpoints:

      • /coupons
      • /courses
      • /courses/GUID/enrollments
      • /courses/GUID/lessons/GUID/enrollments
      • /departments
      • /groups
      • /instructorLedCourses

      • /onlineCourses
      • /roles
      • /sessions
      • /users
      • /users/GUID/enrollments
      • /users/GUID/certificates 


      Even though a highly filtered GET can result in a single record being returned, it is best practice always to specify pagination parameters.

  3. Identify any additional code change requirements.
    • We recommend the technical team review the complete list of endpoint differences found in Appendix C.

 

Step Two: Implement the Changes

  1. Notify your dedicated Client Success Manager or Account Manager of your plans to use Restful API v1.5 in your sandbox. They will arrange for your sandbox API key to be regenerated. To avoid any disruption of existing sandbox development and testing, please ensure that all in-process testing that depends on the current key is completed.

  2. Setup your development environment to deploy code changes to your sandbox environment, where experimentation and testing can take place safely.

  3. For all API calls:
    • Change the service path to rest.myabsorb.com

    • Add “x-api-key: [your private API key]” to the request header

    • Example: “Post https://rest.myabsorb.com/authenticate” (assuming the request header contains the line “x-api-key: [your private API key]”

  4. For calls to paginated endpoints:
    • Set up a loop to keep making GET calls until no records returned (null returned)

    • For each call, supply an “_offset” parameter. This is the current page, starting with 0.

    • For each call, supply the same “_limit” parameter. This is the maximum number of items to be returned per page and can be set as high as 1000.

    • Optionally add sorting by specifying which fields to sort return results by and by which order.

    • Optionally add filtering by supplying the filter query string adhering to OData syntax.

  5. Apply any additional changes identified in Step 3 of the Plan Out the Changes.

 

Step Three: Verify the Changes

  1. First, run all your calls in v1 and v1.5, comparing the results. Any differences found during regression testing should be expected and desired.

  2. Layer on calls to leverage the new v1.5 functionality, such as accessing new endpoints. Ensure the new endpoints deliver the desired results and ensure no regressions are introduced to existing functionality.

  3. Ideally, compare results produced in the Sandbox environment with those on production. For example: if your primary API use case is daily reporting, run your Sandbox changes against a copy of production data and compare the end-of-day reports.

 

Step Four: Deploy to Production

  1. Before you deploy:
  • Notify your dedicated Client Success Manager or Account Manager of your deployment timeline, so any support requests can be flagged and expedited.

  • Ensure you have a rollback strategy in place, including undoing any data changes, just in case the deployment cannot be completed or produces unexpected results.
  1. Deploy to production and start benefitting from the upgraded API.

  2. Let us know how things are going so we can celebrate your success.

 

Appendix A

What is new in Restful API v1.5?

For existing API clients upgrading to v1.5, these new features are available:

  • All API requests will go through a Service Gateway introduced to provide increased security and reliability.
  • Support for pagination, sorting and filtering on selected GET endpoints.
  • User email address is no longer required when updating user attributes.
  • New endpoints allow you to update custom fields and access earned competencies.
  • On the user endpoints, you can now:
    • Sort and filter by custom fields
    • Access last login
  • Optionally, clients can utilize OAuth 2.0 for additional security

For new API clients onboarding directly to v1.5 and upgrading clients that decide not to utilize the backward compatibility setting, all the above features are available plus:

  • Swagger generated and maintained API documentation (these documents are always available but only accurately reflect API usage when backward compatibility is not in use)
  • Requests and responses in camel case
  • Where pagination is used, the response includes a wrapper with the following additional information
  1. totalItems - the total items returned
  2. limit - how many items returned on each page
  3. offset - current offset
  4. returnedItems - how many items were returned on the current page

Pagination

Supply both ‘_offset [numeric value]’ & ‘_limit [numeric value]’ parameters on specific GET requests to paginate returned results.

Offset is the current page, with 0 being the first. Increment this value on successive calls until no records are returned.

If no pagination parameters supplied, default pagination settings are “_offset=0&_limit=20”.

Note: to access records beyond the first page, the endpoint must be called again with an offset value one increment higher than the last call until no results are returned.

For example:

https://[path]/[endpoint]?_offset=0&_limit=100

Sorting

Sorting is a completely optional feature available on endpoints where pagination is supported.

Combined with Filtering, Sorting extends API user abilities to find the information you need in the format you need.

Use ‘_sort’ query string parameter on specific GET requests.

Supply a comma-separated list of sort keys. The default sort order is ascending.

Prefixing '-' before the sort key will return results in descending order.

For example:

https:// https://[path]/[endpoint]?_sort=-relevance&term=abc

 

Filtering

Filtering is a completely optional feature available on endpoints where pagination is supported.

One of the most useful features that will allow more precise queries is Filtering.

The primary use case will be to find the relevant records based on search of a particular category.

Filtering is available on most parameters, but not all. Further details are available in the attached technical documentation. 

Use ‘_filter’ query string parameter on specific GET requests. Supply a comma-separated list of filter operations to be performed. Supported operations include:

  • Operators: eq, ne, gt, ge, it, le, and, or, not, ()
  • Functions: substringof, endswith, startswith, tolower, toupper

For example:

https://[path]/[endpoint]?_filter=startsWith(lastname, ‘leb’) or dataAdded ge datetime‘1999-0306T20:38:07Z’

 

Appendix B

Why is this upgrade necessary?

Restful API v1 has been around since the beginning of Absorb LMS. Since that time, a few things have happened:

  1. The User Experience, modules, services, and underlying data models have changed in response to new clients and new ways of learning.

  2. Technologies and industry standards have changed in response to new threats and opportunities.

  3. Absorb has seen a huge upsurge in LMS usage, placing an ever-increasing burden on existing infrastructure servicing API requests.

  4. In the past year, increased monitoring and API clients have reported processing delays, timeouts, and service interruptions.

After a thorough review, we created a new Restful API version with:

  1. A service gateway now manages and satisfies all API requests
  2. New architecture to handle immediate and planned changes
  3. API versioning so the use of future changes will be optional.
  4. Added non-optional pagination support to high-use “open-ended” GET endpoints

Lastly, it was necessary to move all API clients over to v1.5 and sunset v1 because all API clients share the duplicate request servicing infrastructure. The only way to remove all security threats, service interruptions, and processing bottlenecks is to upgrade all to v1.5.

This upgrade, unfortunately, represents a "breaking change" (because it "breaks the API contract" and requires client code changes). While this was necessary to deliver the required architectural, technological, and functional upgrades required to achieve the responsiveness and reliability our clients demand, we understand this is an exceptional, one-time event. By making major changes now and by introducing API versioning, Absorb aims to prevent mandatory upgrades from happening in the future.

 

Appendix C

List of v1 vs. v1.5 Endpoint Differences

v1 Endpoint

v1.5 Differences

GET /bulk/studentcourses?userids[0]={userids[0]}&userids[1]={userids[1]}&courseids[0]={courseids[0]}&courseids[1]={courseids[1]}

Added new field "DateEdited" in v1.5 

GET /categories

Identical 

GET /categories/{id}

Identical 

GET /certificates/{id}

Identical 

GET /chapters

Identical 

GET /chapters/{id}

Identical 

GET /countries

Identical 

GET /countries/{id}

Identical 

GET /coupons/{CouponId}

Identical 

GET /coupons?offset={offset}&limit={limit}

Identical 

GET /course/{courseid}/prerequisites

Identical 

GET /course/{courseid}/versions

Identical 

GET /CourseBundles

Added new field "DateEdited" and "CourseType" in v1.5 

GET /CourseBundles/{id}

Added new field "DateEdited" and "CourseType" in v1.5 

GET /CourseBundles/forsale?id={id}&catid={catid}&from={from}&to={to}

Added new field "DateEdited" and "CourseType" in v1.5

GET /courses/{courseid}/certificates/{id}

Identical 

GET /courses/{CourseId}/certificates?IncludeExpired={IncludeExpired}&AcquiredDate={AcquiredDate}&ExpiryDate={ExpiryDate}

Identical 

GET /courses/{courseid}/certificates?includeexpired={includeexpired}&acquireddate={acquireddate}&expirydate={expirydate}

Identical 

GET /courses/{courseid}/chapters

Identical 

GET /courses/{courseid}/chapters/{chapterid}/lessons/{id}

Identical 

GET /courses/{courseid}/chapters/{id}

Identical 

GET /courses/{CourseId}/enrollments?Status={Status}&ModifiedSince={ModifiedSince}&Offset={Offset}&Limit={Limit}

Added new field "DateEdited" in v1.5 

GET /courses/{courseid}/lessons

Identical 

GET /courses/{courseid}/lessons/{id}

Identical 

GET /courses/{CourseId}/lessons/{LessonId}/enrollments?Status={Status}&ModifiedSince={ModifiedSince}&Offset={Offset}&Limit={Limit}

Added new field "DateEdited" in v1.5 

GET /courses/{courseid}/resources

Removed field "ModuleId" in V1 

GET /courses/{courseid}/resources/{id}

Removed field "ModuleId" in V1 

GET /courses/{courseid}/sessions

Identical 

GET /courses/{courseid}/sessions/{id}

Identical 

GET /courses/{courseid}/sessions/{sessionid}/enrollments?status={status}&modifiedsince={modifiedsince}

Added new field "DateEdited" in v1.5 

GET /courses/{id}

Added new field "DateEdited" in v1.5 

GET /courses/forsale?id={id}&categoryid={categoryid}&from={from}&to={to}

Added new field "DateEdited" in v1.5 

GET /courses?ModifiedSince={ModifiedSince}&ExternalId={ExternalId}&Offset={Offset}&Limit={Limit}

Added new field "DateEdited" in v1.5 

GET /CourseVersions/{id}

Identical 

GET /credits/credit-types

Identical 

GET /credits/users/{UserId}/enrollments/{EnrollmentId}

Identical 

GET /Curriculums

Added new field "DateEdited" and "CourseType" in v1.5 

GET /Curriculums/CurriculumGroup?id={id}

Added new field "DateEdited" and "CourseType" in v1.5

GET /Curriculums/ForSale?id={id}&catid={catid}&from={from}&to={to}

Added new field "DateEdited" and "CourseType" in v1.5 

GET /Curriculums?id={id}

Added new field "DateEdited" and "CourseType" in v1.5 

GET /CustomFieldDefinitions

Identical 

GET /CustomFieldDefinitions/{id}

Identical 

GET /departments/{id}

Identical 

GET /departments?DepartmentName={DepartmentName}&ExternalId={ExternalId}&Offset={Offset}&Limit={Limit}

Identical 

GET /ecommerce/transactions/{transactionid}

Identical 

GET /ecommerce/transactions?userid={userid}&courseid={courseid}&transactiondate={transactiondate}&transactioncompleteddate={transactioncompleteddate}&paymentgatewaytransactionid={paymentgatewaytransactionid}

Identical 

GET /enrollmentkeys/{enrollmentkeyid}

Identical 

GET /enrollmentkeys?externalid={externalid}

Identical 

GET /enrollments/{courseid}/myresources

Removed field "ModuleId" in V1 

GET /Enrollments/{id}

Added new field "DateEdited" in v1.5 

GET /enrollments/myresources

Removed field "ModuleId" in V1 

GET /Groups

Identical 

GET /Groups/{id}

Identical 

GET /InstructorLedCourses

Added new field "DateEdited" and "CourseType" in v1.5 

GET /InstructorLedCourses/{id}

Added new field "DateEdited" and "CourseType" in v1.5 

GET /instructor-led-course-sessions/{sessionid}/session-schedules/{sessionscheduleid}/attendance

Identical 

GET /languages

Identical 

GET /languages/{id}

Identical 

GET /Lessons

Identical 

GET /Lessons/{id}

Identical 

GET /Messages/{id}

Identical 

GET /mycatalog

Added new field "DateEdited" in v1.5 

GET /mycourses

Added new field "DateEdited" in v1.5 

GET /mydetails

Added new field "DateEdited" and "LastLoginDate" in v1.5 

GET /myenrollments/{courseid}?status={status}&modifiedsince={modifiedsince}

Added new field "DateEdited" in v1.5 

GET /myenrollments?status={status}&modifiedsince={modifiedsince}

Added new field "DateEdited" in v1.5 

GET /OnlineCourses

Identical 

GET /OnlineCourses/{id}

Added new field "DateEdited" and "CourseType" in v1.5 

GET /Prerequisites

Identical 

GET /Prerequisites/{id}

Identical 

GET /provinces/{id}

Identical 

GET /provinces?countryid={countryid}

Identical 

GET /ResourceCategories

Identical 

GET /ResourceCategories/{id}

Identical 

GET /Resources

Removed field "ModuleId" in V1 

GET /Resources/{id}

Removed field "ModuleId" in V1 

GET /Roles

Identical 

GET /Roles/{id}

Identical 

GET /Sessions

Identical 

GET /Sessions/{id}

Identical 

GET /SessionSchedules Identical 

GET /SessionSchedules/{id}

Identical 

GET /Tags

Identical 

GET /Tags/{id}

Identical 

GET /Users/{id}

Added new field "DateEdited" and "LastLoginDate" in v1.5 

GET /users/{userid}/catalog

Added new field "DateEdited" in v1.5 

GET /users/{userid}/certificates/{id}

Identical 

GET /users/{userid}/courses

Added new field "DateEdited" in v1.5 

GET /users/{userid}/enrollments/{courseid}/chapters/{chapterid}/lessons/{lessonid}?status={status}&modifiedsince={modifiedsince}

Added new field "DateEdited" in v1.5 

GET /users/{userid}/enrollments/{courseid}/chapters/{chapterid}/lessons?status={status}&modifiedsince={modifiedsince}

Added new field "DateEdited" in v1.5 

GET /users/{userid}/enrollments/{courseid}/chapters/{chapterid}?status={status}&modifiedsince={modifiedsince}

Added new field "DateEdited" in v1.5 

GET /users/{userid}/enrollments/{courseid}/chapters?status={status}&modifiedsince={modifiedsince}

Added new field "DateEdited" in v1.5 

GET /users/{userid}/enrollments/{courseid}/lessons/{lessonid}/attempts

Added new field "DateEdited" in v1.5 

GET /users/{userid}/enrollments/{courseid}/lessons/{lessonid}?status={status}&modifiedsince={modifiedsince}

Added new field "DateEdited" in v1.5 

GET /users/{userid}/enrollments/{courseid}/lessons?status={status}&modifiedsince={modifiedsince}

Added new field "DateEdited" in v1.5 

GET /users/{userid}/enrollments/{courseid}/resources

Removed field "ModuleId" in V1 

GET /users/{userid}/enrollments/{courseid}/sessions/{sessionid}?status={status}&modifiedsince={modifiedsince}

Added new field "DateEdited" in v1.5 

GET /users/{userid}/enrollments/{courseid}/sessions?status={status}&modifiedsince={modifiedsince}

Added new field "DateEdited" in v1.5 

GET /users/{userid}/enrollments/{courseid}?status={status}&modifiedsince={modifiedsince}

Added new field "DateEdited" in v1.5 

GET /users/{userid}/enrollments?status={status}&modifiedsince={modifiedsince}&offset={offset}&limit={limit}

Added new field "DateEdited" in v1.5 

GET /users/{userid}/grades/{courseid}?status={status}&modifiedsince={modifiedsince}

Added new field "DateEdited" in v1.5 

GET /users/{userid}/messages

Identical 

GET /users/{userid}/messages/received

Identical 

GET /users/{userid}/messages/sent

Identical 

GET /users/{userid}/resources

Removed field "ModuleId" in V1 

GET /users/{userid}/sessiondetails

Identical 

GET /users?Email={Email}&Username={Username}&ModifiedSince={ModifiedSince}&ExternalId={ExternalId}&Offset={Offset}&Limit={Limit}

Added new field "DateEdited" and "LastLoginDate" in v1.5 

GET /Venues

Identical 

GET /Venues/{id}

Identical 

POST /attempts/{attemptid}/finish

Identical 

POST /Authenticate

Identical 

POST /courses/sessiondetails

Identical 

POST /createabsorbaccount?key={key}

"EmailAddress" is not a mandatory field in v1.5 

POST /createdepartment

Identical 

POST /Curriculums/MultipleCurriculumGroups

Identical 

POST /departments

Identical 

POST /ecommerce/transactions/{transactionid}/status

Identical 

POST /enroll/{courseid}

Identical 

POST /enrollmentkeys

Identical 

POST /enrollmentkeys/{enrollmentkeyid}

Identical 

POST /enrollments/unenroll

Identical 

POST /instructor-led-course-sessions/{sessionid}/session-schedules/{sessionscheduleid}/attendance

Identical 

POST /ResourceCategories

Identical 

POST /Tags

Identical 

POST /users/{userid}/enrollments/{courseid}/lessons/{lessonid}/attempts/{attemptid}/finish

Identical 

POST /users/{userid}/enrollments/{courseid}/lessons/{lessonid}/startattempt

Identical 

POST /users/{userid}/enrollments/{courseid}/session/{sessionid}?reenroll={reenroll}&cancelsession={cancelsession}

Identical 

POST /users/{userid}/enrollments/{courseid}?reenroll={reenroll}

Added new field "DateEdited" in v1.5 

POST /users/{userid}/enrollments?reenroll={reenroll}

Identical 

POST /users/upload?key={key}

"EmailAddress" is not a mandatory field in v1.5 

POST /users?key={key}

"EmailAddress" is not a mandatory field in v1.5 

POST /Venues

Identical 

PUT /instructor-led-course-sessions/{sessionid}/session-schedules/{sessionscheduleid}/attendance/{id}

Identical 

PUT /ResourceCategories/{id}

Identical 

PUT /Tags/{id}

Identical 

PUT /Users/{id}

Identical 

PUT /users/{userid}/user-management-settings

Identical 

PUT /Venues/{id}

Identical 

 

Was this article helpful?
1 out of 2 found this helpful