Restful API v1.5 - Upgrade Guide

Restful API v1.5 is a significant upgrade from v1. 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 

 

List of pagination endpoints when called with the 1.5.1 version parameter: 

  • /api/rest/v1_5/lessons
  • /api/rest/v1_5/courses/[GUID]/certificates
  • /api/rest/v1_5/tags
  • /api/rest/v1_5/instructor-led-course-sessions/[GUID]/session-schedules/[GUID]/attendance
  • /api/rest/v1_5/session-schedules
  • /api/rest/v1_5/my-courses, /api/rest/v1_5/my-catalog, 
  • /api/rest/v1_5/curriculums,
  • /api/rest/v1_5/course-bundles

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

  1. 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. Due to updated processes, the Production and Sandbox keys must be different for the Restful API v1.5 to function.

    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 using Restful API v1, please ensure that all in-process testing that depends on the current key is completed.

    This process will not change your Production API key. It's important to note that the Restful v1.5 key is the same as your Restful API v1 key. If you continue to use v1 as you transition to v1.5, please use the same API key for both.

  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, depending on the environment you are using, to "rest.myabsorb.com" for production or "rest.sandbox.myabsorb.com" for the sandbox

    • 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:

    It is critical to update paginated endpoints in your code. These endpoints, which previously returned full data sets, will now return a default of 20 records.

    This means that if no updates are made, the data returned from the API will not always be the full data available to you. Your systems which rely on the full data, may be impacted.


    • Set up a loop to keep making GET calls until no records are 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 maximum number of items to be returned per page 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. For calls to versioned endpoints:
    When developing your code, for certain endpoints the option exists to use the published (or base) version of the endpoint, or a newer version of that endpoint.

    The intention is that for clients with an integration in place, revisions to the API which otherwise would be considered breaking will not impact their current integrations. Clients wishing to benefit from the additional functionality offered by the revision to the endpoint can specify when calling that they'd like the new version.

    Versioned endpoints will be highlighted with an additional header parameter available on the documentation. First time REST API V1.5 users are encouraged to use the newest version of the endpoints to receive the latest features.

    Versioned endpoints will accept a new version header x-api-version. This header is not required, but is recommended for consumers that may not always have the resources to adopt the latest changes to the API.

    In addition, each response from the API will include a version header to help indicate which version you are currently receiving for the given endpoint from the API.

    Note that Absorb may, at its discretion, update the base version of the endpoint. If this occurs, communication will precede the change with enough notice for clients to make changes before their integrations are impacted.

    Also note that while backwards compatibility is enabled, you will be restricted to version 1.5.0.
  6. 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:
    • totalItems - the total items returned
    • limit - how many items returned on each page
    • offset - current offset
    • returnedItems - how many items were returned on the current page
Element Description
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

Versioned APIs The API supports multiple versions concurrently to allow consumers to update their integrations at their own pace. Each request will accept a new version header x-api-version. This header is not required but is recommended for consumers that may not always have the resources to adopt the latest changes to the API.

In addition, each response from the API will include a version header to help indicate which version you are currently receiving for the given endpoint from the API.

Versioned endpoints will be noted with an additional header parameter per endpoint basis within the API documentation. While backward compatibility is enabled, you will be restricted to version 1.5.0.
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://[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’

Rate Limiting The Absorb RESTful API uses a number of safeguards against bursts of incoming traffic to help maximize its stability. Consumers who send many requests in quick succession might see error responses that show up as status code 429 - Too Many Requests.

Requests to the API are limited to 200 per second. There is a burst limit of an additional 100 requests, to act as a buffer if you have a short overrun.

Treat these limits as maximums. See Handling limiting gracefully for advice on handling 429s. If you suddenly see a rising number of rate limited requests, please contact support.
Handling Limiting Gracefully
A basic technique for integrations to gracefully handle limiting is to watch for 429 status codes and build in a retry mechanism. The retry mechanism should follow an exponential backoff schedule to reduce request volume when necessary. We’d also recommend building some randomness into the backoff schedule to smooth out the retried traffic, avoiding another burst.

Another option to consider would be to control traffic to Absorb at a global level, and throttle back if substantial rate limiting is detected in the API responses. Techniques exist which can be implemented client side to control traffic.

 

 

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 improve security and remove service interruptions and processing bottlenecks is to upgrade all users to Version 1.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 

Appendix D

Versioned Endpoints 

Below if a list of currently versioned endpoints and their differences. For all of the following GET endpoints, the API can be called with an "x-api-version" header of 1.5.0 or 1.5.1.

  • /api/rest/v1_5/lessons
  • /api/rest/v1_5/courses/[GUID]/certificates
  • /api/rest/v1_5/tags
  • /api/rest/v1_5/instructor-led-course-sessions/[GUID]/session-schedules/[GUID]/attendance
  • /api/rest/v1_5/session-schedules
  • /api/rest/v1_5/my-courses, /api/rest/v1_5/my-catalog, 
  • /api/rest/v1_5/curriculums,
  • /api/rest/v1_5/course-bundles
Versioned Endpoints (x-api-version) Response Differences 
1.5.0 Unpaginated. Returns up to 55k rows, where possible. 
1.5.1  Pagination. Structured matches pagination conventions. Includes wrapper with total items, limit, offset, and returned items response values.
Was this article helpful?
3 out of 5 found this helpful