When the event occurs within the application, the webhooks service will make a POST request to your specified endpoint. The POST request will be in the UTF-8 charset and will contain a JSON object with details of the event that triggered the webhook.
- Payload date values are in ISO 8601 format and in UTC
- The encoded Id values contained in the message body (userId, courseid) are the same as the id values that are returned from the Litmos API. See the Help Articles for Developer APIs for details.
- Example payload for achievement.earned is as follows.
achievement.earned payload |
{ "id": 4513, "created": "2019-05-06T01:13:19.533", "type": "achievement.earned", "object": "event", "data": { "userId": "yj-nr8PhW8o1", "userName": "sample", "courseId": "nAcqwEA8jUo1", "title": "Course Demo", "code": "", "achievementDate": "2019-05-06T01:12:35.990", "compliantTilldate": null, "result": "Completed", "type": "Course Completed", "firstName": "Sample", "lastName": "User", "achievementId": 368800, "certificateId": "biZrK8ab0LE1" } } |
Request Timeout and Retry Logic
When the Litmos Webhooks service POSTs the event payload to the webhook endpoint, the service will wait for up to 10 seconds for the client to respond with a valid response code. Litmos considers the following response codes to represent a successful receipt of the webhook:
- 200
- 201
- 202
Important Note: Litmos recommends that your endpoint prioritizes sending a valid response before undertaking any processing of the request body to avoid the message delivery being marked as failed and the message delivery re-attempted.
Duplicate Messages
There may be instances where the Litmos Webhooks service may deliver the same webhook multiple times. Litmos recommends that your event processing implements a level of idempotency to cater for these scenarios.
Retry Logic
When the Litmos Webhooks service determines that a message delivery failure has occurred, it will re-attempt delivery of that message. The Litmos Webhooks service considers delivery as failed when:
- An invalid response code is received
- An endpoint takes longer than 10 seconds to provide a valid response
We will re-attempt to deliver the message an additional 5 times using an exponential back-off policy. A message will be considered as permanently failed once all attempts have been exhausted and no further delivery attempts will be made.
-
Retry Logic 1 min (60s) after most recent failure 5 min (300s) after most recent failure 30 min (1800s) after most recent failure 60 min (3600s) after most recent failure 360 min (21600s) after most recent failure
Endpoint Inactivation
To minimize the volume of messages sent to unresponsive endpoints, we will Inactivate your endpoint in the instance where five consecutive permanent failed message deliveries have occurred.
During this time no further messages will be delivered to your endpoint, and no further events will be created until such time as your endpoint is reactivated.
An email advising of inactivation will be sent to the user who initially created the endpoint within the Litmos LMS.
Verification
For additional security, all messages sent by the Litmos Webhooks service contain some additional request headers.
- User-Agent: Will contain the value ‘Litmos’
- Litmos-Signature: consists of a timestamp and a signature. The timestamp is prefixed with t= and the signature Is prefixed with s=
Litmos generates this signature using a hash-based code (HMAC) with SHA-256 hashing algorithm.
To verify the Litmos-Signature:
- Take the integer Unix timestamp from the header
- Append it the body of the payload so the format is: {timestamp}.{MessageBody}
- Then using SHA-256 encryption, use the Signing Secret from your webhook endpoint page as the secret and the string above as the text to generate a hash
- Compare this Hash with the value of s from the header. If the values do not match, then you should ignore this request to your endpoint.
Generating the Hash Code
private static string EncryptMessage(string messageBody, string signingSecret) { // Represents an ASCII character encoding of Unicode characters. ASCIIEncoding encoding = new System.Text.ASCIIEncoding(); byte[] textBytes = encoding.GetBytes(messageBody); byte[] keyBytes = encoding.GetBytes(signingSecret); byte[] hashBytes; //Computes a Hash-based Message Authentication Code (HMAC) by using the System.Security.Cryptography.SHA256 hash function. using(var hash = new System.Security.Cryptography.HMACSHA256(keyBytes)) { //Computes the hash value for the specified byte array. hashBytes = hash.ComputeHash(textBytes); } // Convert to hexadecimal character string. return System.BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); }
Samples
The raw content for each of the webhook payloads is shown in JSON format.
Achievement.Earned (Course)
{ "id": 7315, "created": "2020-10-27T09:15:10.297", "type": "achievement.earned", "object": "event", "data": { "userId": "jgEBm_Yoi3s1", "userName": "20201019-Learner2", "courseId": "g9zUgeZTFR01", "title": "Example Course", "code": "12345", "achievementDate": "2020-11-15T08:36:10.313", "compliantTilldate": null, "result": "Completed", "type": "Course Completed", "firstName": "Lenny", "lastName": "Litmos", "achievementId": 372112, "certificateId": null } }
Achievement.Earned (Learning path)
{ "id": 7315, "created": "2020-10-27T09:15:10.859", "type": "achievement.earned", "object": "event", "data": { "userId": "jgEBm_Yoi3s1", "userName": "LeonardSomtil", "learningPathId": "g9zUgeZTFR01", "title": "Example Learning Path", "code": "12345", "achievementDate": "2020-11-15T08:36:10.313", "compliantTilldate": null, "result": "Completed", "type": "Learning Path Completed", "firstName": "Leonard", "lastName": "Somtil", "achievementId": 372113, "certificateId": null } }
Session.Created (Instructor led training session)
{ "id": "BB4slpiG7_81", "created": "2020-11-15T20:54:24.64", "type": "Session.Created", "object": "event", "data": { "courseId": "4gcvbwSFbGM1", "courseName": "Example Course", "moduleId": "Qot7aC9eWB81", "moduleName": "Example ILT Module Name", "sessionId": "BB4slpiG7_81", "sessionName": "Example ILT Session Name", "sessiondayId": "VBQa1lIxkJQ1", "sessionType": "Class Room", "sessionUrl": "", "sessionNotifications": "Admin1Username,Admin2Username", "instructors": "cFjqwRheVgs1", "instructorUsernames": "Instructor1Username", "startDate": "2020-11-15", "endDate": "2020-11-15", "startTime": "09:00:00:000", "endTime": "11:00:00:000", "timeZone": "Pacific Standard Time", "location": "c2W2LaORoGE1", "locationName": "", "resources": "", "resourceNames": "", "details": "Bring your computer to research sites about Webhooks", "seatMinimum": "10", "seatMaximum": "20", "closeRegistration": "0", "closeUnregistration": "0", "approvals": "", "approvalsUsernames": "" } }
Session.Registration (Instructor led training session)
{ "id": "Df_RGP2K2Zk1", "created": "2020-11-15T22:29:28.363", "type": "Session.Registration", "object": "event", "data": { "courseId": "MEGfeo4cRxc1", "courseName": "Example Course", "moduleId": "j6HLt844gNM1", "moduleName": "Example ILT Module Name", "sessionId": "Df_RGP2K2Zk1", "sessionName": "Example ILT Session Name", "data": { "userID": "jgEBm_Yoi3s1", "userName": "LeonardSomtil", "firstName": "Leonard", "lastName": "Somtil", "email": "leonard.somtil@Litmos.com", "manager": "j6HLt844gNM1", "companyName": "LITMOSLitmos", "workPhone": "9252512220", "mobilePhone": "", "timeZone": "Pacific Standard Time", "language": "English (United States)" } } }
eLearningCourse.Processed (AICC, Scorm & xAPI)
{ "id": "YVLbfqmWZ1c1", "created": "2020-11-15T09:14:48.48", "type": "ElearningCourse.Processed", "object": "event", "data": { "moduleId": "b892u2iGSV01", "originalId": "1169793", "moduleName": "Example Tin Can Course File", "moduleDescription": "Example Module Description Text", "code": "TC123", "createdBy": "68779", "createdByUsername": "Leonard.Somtil@Litmos.com", "createdDate": "2020-11-15T09:12:46.337", "status": "Success", "active": "1" } }
Learner.Overdue (Course)
{ "id": 4513, "created": "2020-02-19T17:34:46.12", "type": "Learner.overdue", "object": "event", "data": { "userId": "jgEBm_Yoi3s1", "userName": "LeonardSomtil", "firstName": "Leonard", "lastName": "Somtil", "courseId": "eo4cREA8jUo1", "courseName": "Course Demo 2", "code": "CourseRefCode", "overdueType": "Specific Date", "overdueDate": "2020-02-19T17:20:11.00", "automaticRetake": "180" } }
Important Note: The Learner.Overdue event notification can be triggered through two events: a course with a due date enabled, or a course with an initial compliance date enabled.
Learner.NotCompliant (Course)
{ "id": "KSJdxoqPA9y7", "created": "2020-02-19T17:34:46.12", "type": "Learner.notcompliant", "object": "event", "data": { "userId": "jgEBm_Yoi3s1", "userName": "LeonardSomtil", "firstName": "Somtil", "lastName": "User", "courseId": "nAcqwEA8jUo1", "courseName": "Course Demo", "code": "CourseRefCode", "compliantTilldate": "2020-02-19T17:00:00.00", "complianceType": "Time Span", "complianceBasedOn": "Completion Date", "compliantFor": "365", "automaticEmailreminder": "7", "automaticRetake": "180" } }