Webhooks
About Webhooks
Webhooks provide a way for notifications to be delivered to your server whenever certain system events occur within the Cognassist Platform.
Currently, the available system events include:
- "Assessment Completed", which is triggered when a learner who belongs to your organisation completes their assessment.
- "Assessment Report Created", which is triggered after a request to generate the assessment report for a learner has successfully completed.
- "Assessment Report Request Failed", which is triggered after a request to generate the assessment report for a learner has failed.
- "Modules Assigned", which is triggered when a learner is assigned new modules.
- "Module First Accessed", which is triggered when a learner first accesses a module.
- "Module First Completed", which is triggered when a learner first completes a module.
- "Learner Export Completed", which is triggered after a request to generate a learner export has successfully completed.
- "Learner Export Failed", which is triggered after a request to generate a learner export has failed.
When you create or update a webhook, you can choose which system events you would like to subscribe to. Subscriptions to different system events will trigger different amounts of requests; to limit the number of requests made to your server, please ensure to only subscribe to system events you want to handle.
When we make a request to your server, we record it as an "invocation log". You can use the Cognassist API to retrieve invocation logs for a webhook to check if all of the requests we made were successful.
API Endpoints
Version 1 of the Cognassist API includes endpoints that can be used for managing webhooks.
Getting a List of Available System Events
GET /systemevents
This endpoint will return a list of available system events that can be subscribed to.
Creating a Webhook
POST /webhooks
This endpoint will create a new webhook for your organisation. The following details must be specified in the request body:
- The
SystemEventIdslist allows you to specify one or more system events you would like the webhook to subscribe to. Please use the Getting a List of Available System Events endpoint to get a list of system event ids webhooks can subscribe to. - The
Urlis the URL of the server that will receive the requests from Cognassist when a system event subscription is triggered. - The
Secretallows you to ensure that the POST requests sent to the specifiedUrlare coming from Cognassist. Each request will include ax-cognassist-sha256header with a hash signature. Please see the Request Signature section for more information about the header. - The
Descriptionproperty simply allows you to give the webhook a description or an identifier to help you remember what the purpose of the webhook is.
Updating a Webhook
PUT /webhooks/{webhookId}
This endpoint will update an existing webhook. It expects the same details in the request body as the Creating a Webhook endpoint.
Getting a Webhook or a List of Webhooks
GET /webhooks/{webhookId}
This endpoint will return details about the specified webhook.
GET /webhooks
This endpoint will return all the webhooks that were created for your organisation.
Deleting a Webhook
DELETE /webhooks/{webhookId}
This endpoint will delete a single, existing webhook. Please note that all of the invocation logs for the specified webhook will also be deleted.
Getting a Webhook Invocation Log or a List of Webhook Invocation Logs
GET /webhooks/{webhookId}/invocationlogs/{invocationLogId}
This endpoint will return a single invocation log for the specified webhook, including the data that was sent to your server.
GET /webhooks/{webhookId}/invocationlogs
This endpoint will return a paginated list of all the invocation logs for the specified webhook. The list will not include the data that was sent to your server.
Request from Cognassist
Cognassist will send an HTTP POST request to the URL that was provided when a webhook was created or updated when any of the system events that the webhook is subscribed to occur.
The request from Cognassist will use the application/json content type.
Request Payload
The payload of the HTTP POST request will have the following properties:
Datais an object. Its properties will differ for each system event.SystemEventis an object that includes the id and name of the system event that triggered the HTTP POST request.DataIdis a globally unique identifier of the HTTP POST request. It can be used to avoid processing the same piece of data twice as we use retry strategies, which could cause the same piece of data to be sent more than once.
{
"Data": {
// The structure of this object will depend on the system event
},
"SystemEvent": {
"Id": "int",
"DisplayName": "string"
},
"DataId": "guid"
}
"Assessment Completed" Payload
The payload for the "Assessment Completed" system event has the following structure:
LearnerIdis the globally unique identifier of the learner who has just completed their assessment.
{
"Data": {
"LearnerId": "00000000-0000-0000-0000-000000000000"
},
"SystemEvent": {
"Id": 100,
"DisplayName": "Assessment Completed"
},
"DataId": "00000000-0000-0000-0000-000000000000"
}
"Assessment Report Created" Payload
The payload for the "Assessment Report Created" system event is multipart/form-data request.
It has two named parts to it:
- "data", which contains information about the "Assessment Report Created" system event, and is described below.
- "file", which is the binary content that forms the PDF assessment report.
The "Assessment Report Created" system event data has the following structure:
LearnerIdis the globally unique identifier of the learner for whom the assessment report has been created.RequestIdis the globally unique identifier provided when the request to generate the report was made via the Cognassist API.
{
"Data": {
"LearnerId": "00000000-0000-0000-0000-000000000000",
"RequestId": "00000000-0000-0000-0000-000000000000"
},
"SystemEvent": {
"Id": 200,
"DisplayName": "Assessment Report Created"
},
"DataId": "00000000-0000-0000-0000-000000000000"
}
"Assessment Report Request Failed" Payload
The payload for the "Assessment Report Request Failed" system event has the following structure:
LearnerIdis the globally unique identifier of the learner for whom the assessment report has been requested.RequestIdis the globally unique identifier provided when the request to generate the report was made via the Cognassist API.
{
"Data": {
"LearnerId": "00000000-0000-0000-0000-000000000000",
"RequestId": "00000000-0000-0000-0000-000000000000"
},
"SystemEvent": {
"Id": 300,
"DisplayName": "Assessment Report Request Failed"
},
"DataId": "00000000-0000-0000-0000-000000000000"
}
"Modules Assigned" Payload
The payload for the "Modules Assigned" system event has the following structure:
LearnerIdis the globally unique identifier of the learner who has been assigned new modules.Modulesis the array of modules that have been assigned to the learner, and each has the following properties:Idis the globally unique identifier of the moduleTitleis the title of the module.Categoryis the category the module is assigned to.Urlis the url the learner can navigate to to access to module.
{
"Data": {
"LearnerId": "00000000-0000-0000-0000-000000000000",
"Modules": [
{
"Id": "00000000-0000-0000-0000-000000000000",
"Title": "Module title",
"Category": "Module category",
"Url": "http://foo.com/play/123456"
}
]
},
"SystemEvent": {
"Id": 400,
"DisplayName": "Modules Assigned"
},
"DataId": "00000000-0000-0000-0000-000000000000"
}
"Module First Accessed" Payload
The payload for the "Module First Accessed" system event has the following structure:
LearnerIdis the globally unique identifier of the learner who has accessed a module.ModuleIdis the globally unique identifier of the module the learner has accessed.
{
"Data": {
"LearnerId": "00000000-0000-0000-0000-000000000000",
"ModuleId": "00000000-0000-0000-0000-000000000000"
},
"SystemEvent": {
"Id": 500,
"DisplayName": "Module First Accessed"
},
"DataId": "00000000-0000-0000-0000-000000000000"
}
"Module First Completed" Payload
The payload for the "Module First Completed" system event has the following structure:
LearnerIdis the globally unique identifier of the learner who has accessed a module.ModuleIdis the globally unique identifier of the module the learner has accessed.
{
"Data": {
"LearnerId": "00000000-0000-0000-0000-000000000000",
"ModuleId": "00000000-0000-0000-0000-000000000000"
},
"SystemEvent": {
"Id": 600,
"DisplayName": "Module First Completed"
},
"DataId": "00000000-0000-0000-0000-000000000000"
}
"Learner Export Completed" Payload
The "Learner Export Completed" system event has the following structure:
RequestIdis the globally unique identifier provided when the request to generate the export was made via the Cognassist API.ExportDatais the array of learner evidence records that make up the export. Each record has the following properties:FirstName,LastName,Email,ClientReference,LearnerUniqueReferenceare strings identifying the learner.IdentifiedDomainsis an array of cognitive domain IDs (as strings) — see Cognitive Domain Codes below.IdentifiedIndexesis an array of cognitive index IDs (as strings) — see Cognitive Index Codes below.SupportProvidedByNameandSupportProvidedByEmailare the name and email of the person who provided the support.TutorManagerNamesandTutorManagerEmailsare comma-separated strings of the learner's tutor manager names and emails.SupportStartDate,SupportEndDate,SupportRecordedDateare ISO 8601 date-time strings, ornullwhen not set.SupportTypeis a support record type ID (as a string) — see Support Record Type Codes below.ModulesSelectedis an array of module titles selected for the support session.ReasonableAdjustmentsSelectedis an array of reasonable adjustments applied during the session.SupportProvidedandSupportReflectionsare free-text fields capturing the support provided and the learner's reflections.SupportSessionTypeis a session type ID (as a string), ornullwhen not set — see Support Session Type Codes below.DurationOfSupportis a meeting time ID (as a string), ornullwhen not set — see Duration of Support Codes below.CoverageCheckStatusis a coverage status ID (as a string) — see Coverage Check Status Codes below.AboveAndBeyondDeclarationConfirmedByNameandAboveAndBeyondDeclarationConfirmedByEmailidentify who confirmed the above-and-beyond declaration.
{
"Data": {
"RequestId": "00000000-0000-0000-0000-000000000000",
"ExportData": [
{
"FirstName": "Jane",
"LastName": "Doe",
"Email": "jane.doe@example.com",
"ClientReference": "12345",
"LearnerUniqueReference": "9876543210",
"IdentifiedDomains": ["1", "2"],
"IdentifiedIndexes": ["1", "2"],
"SupportProvidedByName": "Alex Smith",
"SupportProvidedByEmail": "alex.smith@example.com",
"TutorManagerNames": "Taylor Brown",
"TutorManagerEmails": "taylor.brown@example.com",
"SupportStartDate": "2026-03-01T00:00:00",
"SupportEndDate": "2026-03-31T00:00:00",
"SupportRecordedDate": "2026-03-25T10:30:00",
"SupportType": "20",
"ModulesSelected": ["Example module title"],
"ReasonableAdjustmentsSelected": [
"Use simplified language",
"break tasks into smaller steps."
],
"SupportProvided": "Example placeholder narrative describing the support session, interventions, outcomes, and next steps.",
"SupportReflections": "Example placeholder learner reflection describing what was useful and what they plan to do next.",
"SupportSessionType": "30",
"DurationOfSupport": "50",
"CoverageCheckStatus": "30",
"AboveAndBeyondDeclarationConfirmedByName": "Alex Smith",
"AboveAndBeyondDeclarationConfirmedByEmail": "alex.smith@example.com"
},
{
"FirstName": "John",
"LastName": "Smith",
"Email": "john.smith@example.com",
"ClientReference": "67890",
"LearnerUniqueReference": "",
"IdentifiedDomains": ["8"],
"IdentifiedIndexes": [],
"SupportProvidedByName": "Morgan Lee",
"SupportProvidedByEmail": "morgan.lee@example.com",
"TutorManagerNames": "Casey Johnson",
"TutorManagerEmails": "casey.johnson@example.com",
"SupportStartDate": "2026-04-01T00:00:00",
"SupportEndDate": "2026-04-30T00:00:00",
"SupportRecordedDate": "2026-04-10T14:15:00",
"SupportType": "10",
"ModulesSelected": [],
"ReasonableAdjustmentsSelected": [
"Allow extra processing time",
"present information in short chunks."
],
"SupportProvided": "Example placeholder narrative for a second learner record.",
"SupportReflections": "Example placeholder reflection for a second learner record.",
"SupportSessionType": null,
"DurationOfSupport": null,
"CoverageCheckStatus": "20",
"AboveAndBeyondDeclarationConfirmedByName": "Morgan Lee",
"AboveAndBeyondDeclarationConfirmedByEmail": "morgan.lee@example.com"
}
]
},
"SystemEvent": {
"Id": 700,
"DisplayName": "Learner Export Completed"
},
"DataId": "00000000-0000-0000-0000-000000000000"
}
Cognitive Domain Codes
| Code | Domain |
|---|---|
| 1 | Verbal Memory |
| 2 | Literacy |
| 3 | Numeracy |
| 4 | Visual Information Processing Speed |
| 5 | Non Verbal Memory |
| 6 | Executive Function |
| 7 | Verbal Reasoning |
| 8 | Visual Perception |
| 9 | Reading Decoding |
Cognitive Index Codes
| Code | Index |
|---|---|
| 1 | Language |
| 2 | Visual |
| 3 | Memory |
| 4 | Speed of working |
| 5 | Non-speed of working |
| 6 | Speed of working difference |
Support Record Type Codes
| Code | Type |
|---|---|
| 10 | Regular |
| 20 | Above and beyond |
Support Session Type Codes
| Code | Session Type |
|---|---|
| 10 | Progress review 1:1 |
| 20 | Pastoral support 1:1 |
| 30 | Learning support 1:1 |
| 40 | Careers guidance 1:1 |
| 50 | Functional skills coaching |
| 60 | Reasonable adjustment review |
| 70 | Return-to-learning meeting |
| 80 | Off-the-job training review |
| 90 | Workplace support visit |
| 100 | Skills gap coaching |
Duration of Support Codes
| Code | Duration |
|---|---|
| 10 | 0.5 hours |
| 20 | 1 hour |
| 30 | 1.5 hours |
| 40 | 2 hours |
| 50 | 2.5 hours |
| 60 | 3 hours |
| 70 | 3.5 hours |
| 80 | 4 hours |
| 90 | 4.5 hours |
| 100 | 5 hours |
| 110 | 5.5 hours |
| 120 | 6 hours |
| 130 | 6.5 hours |
| 140 | 7 hours |
| 150 | 7.5 hours |
| 160 | 8 hours |
Coverage Check Status Codes
| Code | Status |
|---|---|
| 10 | Blank |
| 20 | Needs improvement |
| 30 | Meets standard |
"Learner Export Failed" Payload
The payload for the "Learner Export Failed" system event has the following structure:
RequestIdis the globally unique identifier provided when the request to generate the export was made via the Cognassist API.
{
"Data": {
"RequestId": "00000000-0000-0000-0000-000000000000"
},
"SystemEvent": {
"Id": 800,
"DisplayName": "Learner Export Failed"
},
"DataId": "00000000-0000-0000-0000-000000000000"
}
Request Signature
We use the Secret property of your webhook to create a hash signature with each payload. This hash signature is included with the headers of each request as x-cognassist-sha256.
You should calculate a hash signature using the same Secret and ensure that the result matches the hash from Cognassist. If the two signatures do not match, please do not process the request.
Verifying the Request Signature
Here is an example of how the signature can be verified:
[HttpPost]
public async Task<IActionResult> Index()
{
// First, check if the signature is included in the request headers.
var headerExists = Request.Headers.TryGetValue("x-cognassist-sha256", out var signatureHeader);
if (!headerExists)
{
// Do not process the request if there is no signature.
throw new Exception("Header 'x-cognassist-sha256' was not provided in the request.");
}
var requestBody = await Request.GetRawBodyAsync();
VerifySignature(requestBody, signatureHeader.ToString(), Environment.GetEnvironmentVariable("WebhookSecret"));
var data = JsonSerializer.Deserialize<WebhookRequest>(requestBody);
// Do something with the data...
return Ok();
}
// Use the request body, the request signature and the webhook secret to verify the signature.
private static void VerifySignature(string requestBody, string requestSignature, string secret)
{
// Create a SHA256 hash signature using the webhook secret and the request body.
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var payloadBytes = Encoding.UTF8.GetBytes(requestBody);
var hashBytes = hmac.ComputeHash(payloadBytes);
var signature = Convert.ToBase64String(hashBytes);
if (!CompareSignatures(signature, requestSignature))
{
// Do not process the request if the signatures do not match.
throw new Exception("Signatures did not match.");
}
}
// Check if the signature from the request and the calculated signature are the same.
private static bool CompareSignatures(string signature1, string signature2)
{
var bytes1 = Encoding.UTF8.GetBytes(signature1);
var bytes2 = Encoding.UTF8.GetBytes(signature2);
if (bytes1.Length != bytes2.Length)
{
return false;
}
for (int i = 0; i < bytes1.Length; i++)
{
if (bytes1[i] != bytes2[i])
{
return false;
}
}
return true;
}
private sealed record WebhookRequest(object Data, SystemEvent SystemEvent, Guid DataId);
private sealed record SystemEvent(int? Id, string? DisplayName);
The signature needs to be verified differently for a multipart payload:
[HttpPost]
public async Task<IActionResult> Index()
{
// First, check if the signature is included in the request headers.
var headerExists = Request.Headers.TryGetValue("x-cognassist-sha256", out var signatureHeader);
if (!headerExists)
{
// Do not process the request if there is no signature.
throw new Exception("Header 'x-cognassist-sha256' was not provided in the request.");
}
if (Request.HasFormContentType)
{
// verify the signatures
Request.EnableBuffering();
using (var ms = new MemoryStream())
{
await Request.Body.CopyToAsync(ms);
var contentBytes = ms.ToArray();
VerifySignature(contentBytes, signatureHeader.ToString(), secret);
Request.Body.Position = 0;
}
var form = await Request.ReadFormAsync();
var formFile = form.Files["file"];
// Do something with the file...
var formData = form["data"];
var data = JsonSerializer.Deserialize<WebhookRequest>(formData!);
// Do something with the data...
}
return Ok();
}