Search documentation

Search documentation pages and headings.

Skip to content

Integration · Reference

Webhooks

Send signed learner and notification events to your systems.

Webhooks queue learner and notification events for delivery to your application. They are the preferred alternative to repeatedly polling the API for changes.

Create a Webhook#

  1. Open Admin > Webhooks.
  2. Create a webhook and enter a public endpoint URL.
  3. Add an optional description.
  4. Select the events to receive, or leave the event list empty to receive all events.
  5. Add custom request headers if your endpoint requires them.
  6. Enable the webhook and save it.
  7. Copy the signing secret and store it securely.

An organization can configure up to five webhooks. Disable a webhook to pause new deliveries without deleting its configuration or logs.

Event Types#

Kokobi supports learner progress events and the events used by course and collection notifications.

EventSent when
learner.startedA learner starts an activity.
learner.updatedA learner's saved attempt changes.
learner.completedAn attempt becomes terminal: completed, passed, or failed.
course.inviteA learner is invited to a course.
course.requestA learner requests access to a course.
course.request.acceptedA course access request is accepted.
course.request.rejectedA course access request is rejected.
course.completionA course completion notification is produced.
course.inactiveAn inactive-course notification is produced.
collection.inviteA learner is invited to a collection.
collection.requestA learner requests access to a collection.
collection.request.acceptedA collection access request is accepted.
collection.request.rejectedA collection access request is rejected.
collection.completionA collection completion notification is produced.

Selecting specific events limits deliveries to that list. An empty event selection means all current events, including event types added later.

Request Format#

Kokobi sends an HTTP POST request with a JSON body:

json
{  "event": "learner.completed",  "data": {}}

The shape of data depends on the event:

  • Learner events contain the learner, resource connection, latest attempt, module, status, score, and SCORM runtime data when available.
  • Course and collection notification events contain recipient addresses and compact event-specific data, usually resource or learner identifiers.

Inspect the delivery JSON in Admin > Webhooks > Webhook > Logs while developing an integration. Treat added fields as backward-compatible and ignore properties your handler does not use.

Request Headers#

Every delivery includes:

text
Content-Type: application/jsonwebhook-timestamp: 2026-07-24T12:00:00.000Zwebhook-signature: <hex-encoded-signature>

Kokobi also includes the custom headers configured on the webhook.

Do not configure custom headers named Content-Type, webhook-timestamp, or webhook-signature; those names are reserved for delivery and signature verification.

Verify Signatures#

The webhook-signature value is an HMAC-SHA256 digest of the timestamp and the exact request body:

text
HMAC-SHA256(secret, timestamp + "." + raw_request_body)

Always verify the signature before parsing or processing the event. Use the raw bytes received over HTTP; serializing a parsed object again can produce different bytes.

Node.js and Express Example#

javascript
import { createHmac, timingSafeEqual } from "node:crypto";import express from "express";
const app = express();
app.post(  "/kokobi-webhook",  express.raw({ type: "application/json" }),  (request, response) => {    const timestamp = request.get("webhook-timestamp");    const signature = request.get("webhook-signature");    const body = request.body;
    if (!timestamp || !signature || !Buffer.isBuffer(body)) {      return response.sendStatus(400);    }
    const expected = createHmac("sha256", process.env.KOKOBI_WEBHOOK_SECRET)      .update(timestamp + ".")      .update(body)      .digest();    const provided = Buffer.from(signature, "hex");    const valid =      provided.length === expected.length &&      timingSafeEqual(provided, expected);
    if (!valid) return response.sendStatus(401);
    const payload = JSON.parse(body.toString("utf8"));    response.sendStatus(204);
    // Process payload.event and payload.data asynchronously.    queueEvent(payload);  },);

Also reject timestamps outside a short tolerance window to reduce replay risk. Keep the signing secret in a secret manager and never expose it in client-side code.

Delivery and Retries#

A delivery succeeds when the endpoint returns an HTTP status from 200 through 299. Network failures and non-2xx responses remain pending and are retried automatically with increasing delays.

For reliable handling:

  1. Verify the signature.
  2. Persist or enqueue the event.
  3. Return a 2xx response quickly.
  4. Process slow work outside the request.
  5. Make processing idempotent because retries can produce duplicate requests.

Kokobi records pending, successful, and failed deliveries and retries within a bounded schedule. Completed records become eligible for cleanup after seven days.

Logs and Troubleshooting#

Open a webhook to review its overview and logs. Each delivery records the event, payload, status, retry count, next retry time, creation time, and completion time.

Deliveries remain pending#

  • Confirm the endpoint is public and uses HTTPS in production.
  • Confirm it accepts POST requests with a JSON content type.
  • Return a 2xx response after safely recording the event.
  • Check firewall, proxy, and application logs for rejected requests.

Signature verification fails#

  • Use the signing secret from the same webhook that sent the delivery.
  • Compute the digest from timestamp + "." + raw body with no extra newline.
  • Read the timestamp and signature from the exact header names above.
  • Confirm no middleware parsed or modified the body before verification.

Events are missing#

  • Confirm the webhook is enabled.
  • Review its selected event list; an empty list receives all events.
  • Confirm the action occurred in the same organization as the webhook.
  • Check delivery logs before investigating the receiving application.