Skip to content

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 SystemEventIds list 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 Url is the URL of the server that will receive the requests from Cognassist when a system event subscription is triggered.
  • The Secret allows you to ensure that the POST requests sent to the specified Url are coming from Cognassist. Each request will include a x-cognassist-sha256 header with a hash signature. Please see the Request Signature section for more information about the header.
  • The Description property 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:

  • Data is an object. Its properties will differ for each system event.
  • SystemEvent is an object that includes the id and name of the system event that triggered the HTTP POST request.
  • DataId is 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:

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

  • LearnerId is the globally unique identifier of the learner for whom the assessment report has been created.
  • RequestId is 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:

  • LearnerId is the globally unique identifier of the learner for whom the assessment report has been requested.
  • RequestId is 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:

  • LearnerId is the globally unique identifier of the learner who has been assigned new modules.
  • Modules is the array of modules that have been assigned to the learner, and each has the following properties:
  • Id is the globally unique identifier of the module
  • Title is the title of the module.
  • Category is the category the module is assigned to.
  • Url is 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:

  • LearnerId is the globally unique identifier of the learner who has accessed a module.
  • ModuleId is 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:

  • LearnerId is the globally unique identifier of the learner who has accessed a module.
  • ModuleId is 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:

  • RequestId is the globally unique identifier provided when the request to generate the export was made via the Cognassist API.
  • ExportData is the array of learner evidence records that make up the export. Each record has the following properties:
  • FirstName, LastName, Email, ClientReference, LearnerUniqueReference are strings identifying the learner.
  • IdentifiedDomains is an array of cognitive domain IDs (as strings) — see Cognitive Domain Codes below.
  • IdentifiedIndexes is an array of cognitive index IDs (as strings) — see Cognitive Index Codes below.
  • SupportProvidedByName and SupportProvidedByEmail are the name and email of the person who provided the support.
  • TutorManagerNames and TutorManagerEmails are comma-separated strings of the learner's tutor manager names and emails.
  • SupportStartDate, SupportEndDate, SupportRecordedDate are ISO 8601 date-time strings, or null when not set.
  • SupportType is a support record type ID (as a string) — see Support Record Type Codes below.
  • ModulesSelected is an array of module titles selected for the support session.
  • ReasonableAdjustmentsSelected is an array of reasonable adjustments applied during the session.
  • SupportProvided and SupportReflections are free-text fields capturing the support provided and the learner's reflections.
  • SupportSessionType is a session type ID (as a string), or null when not set — see Support Session Type Codes below.
  • DurationOfSupport is a meeting time ID (as a string), or null when not set — see Duration of Support Codes below.
  • CoverageCheckStatus is a coverage status ID (as a string) — see Coverage Check Status Codes below.
  • AboveAndBeyondDeclarationConfirmedByName and AboveAndBeyondDeclarationConfirmedByEmail identify 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:

  • RequestId is 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();
}