Grow LMS API Documentation

Grow by Plenum — Real-Time Data: Webhooks

Receiving signed, push-based events the moment they happen in Grow.

This document is for the engineers building the receiving side of a real-time integration with Grow by Plenum. It covers how events are delivered, what the payloads look like, the catalog of events most relevant to a compliance-training program, how to verify that an event genuinely came from Grow, and how to operate the endpoint reliably.

Throughout, <SCHOOL_ID> is your dedicated Grow environment, <YOUR_ENDPOINT> is the HTTPS URL you want events delivered to, and <WEBHOOK_SECRET> is the pre-shared signing secret Plenum configures for that endpoint. No real secret or live data appears in this document.


1. The short version

A webhook is Grow pushing data to you the instant something happens, instead of you polling to ask whether anything has changed. When a learner completes a course, earns a certificate, or loses access to a product, Grow sends an HTTP POST carrying a structured JSON payload to an endpoint you control — in near real time.

Three properties matter up front:

  • It’s push, not poll. You stand up a receiver; Grow delivers to it. No scheduled job, no latency budget spent waiting for the next batch.
  • It’s signed. Every delivery carries a cryptographic signature you verify against a shared secret, so you can trust the payload is authentic and unmodified (Section 5).
  • Setup is one-time. Subscribing your endpoint to events is a configuration Plenum performs once for your <SCHOOL_ID> environment during onboarding. After that it runs unattended.

2. The delivery model

Each event is an HTTP POST to <YOUR_ENDPOINT> with a JSON body. Every payload shares the same outer envelope, which makes a single receiver easy to write:

{
  "version": 2,
  "type": "courseCompleted",
  "trigger": "course_completed",
  "school_id": "<SCHOOL_ID>",
  "data": { }
}
Field Meaning
version Payload-format version. Branch on this so a future format change can’t silently break your parser.
type The event in camelCase (e.g. courseCompleted) — the field most integrations switch on.
trigger The same event in snake_case (e.g. course_completed); a convenience for systems that prefer it.
school_id Identifies the environment the event came from — useful if one receiver serves more than one Grow environment.
data The event-specific body. Its shape depends on type; the events you’ll care about are detailed below.

Your endpoint should respond promptly with a 2xx to acknowledge receipt. Treat anything you do with the payload (writing to a warehouse, kicking off a workflow) as work to be done after you’ve acknowledged, so a slow downstream step doesn’t hold the connection open.


3. Event catalog (compliance-program focus)

Grow emits a broad set of events, including commerce-oriented ones (purchases, subscriptions, trials) that aren’t relevant to a workforce-compliance program. The events that matter for tracking training, certification, and access are:

Event type Fires when… What you primarily get in data
courseCompleted A learner completes a course completed_at, a manually_completed flag, and the course + user
learningProgramCompleted A learner finishes a multi-course program / learning path The learning program and the user
awardedCertificate A certificate is issued to a learner The certificate (id, title, type, issued timestamp, score, status) and the user
userUpdated A learner is registered, or their profile changes The full user record (contact, custom fields, tags, attribution)
userUnenrolledFromProduct A learner loses access to a course/bundle/program The product and the user
userTagAdded / userTagDeleted A classification tag is applied to or removed from a learner The user and their current tag set
enrolledFreeCourse A learner is enrolled into a no-cost course The course and the user

For a compliance dashboard, the workhorses are courseCompleted, learningProgramCompleted, and awardedCertificate for “who has met their requirement,” userUnenrolledFromProduct for “who has lost access,” and the tag events if you drive cohorts (region, crew, contractor vs. employee) off tags.


4. Worked example: courseCompleted

This is the event most compliance integrations build around. An illustrative payload (placeholders substituted for live data):

{
  "version": 2,
  "type": "courseCompleted",
  "trigger": "course_completed",
  "school_id": "<SCHOOL_ID>",
  "data": {
    "completed_at": 1751299200.0,
    "manually_completed": false,
    "ip_address": null,
    "course": {
      "id": "fall-protection-awareness",
      "title": "Fall Protection Awareness",
      "access": "private",
      "created": 1746057600.0
    },
    "user": {
      "id": "<USER_ID>",
      "email": "learner@your-org.example",
      "tags": ["region-gulf", "crew-a"],
      "fields": { "cf_employee_id": "<EMP_ID>" }
    }
  }
}

Two fields carry most of the weight for a compliance audit trail:

  • completed_at — the authoritative completion timestamp, as a Unix epoch value. This is what your renewal clocks and “current as of” reporting should key off.
  • manually_completed — a boolean that distinguishes a naturally earned completion (false) from one an administrator applied by override (true). Keeping this in your record lets an auditor tell the two apart, rather than treating every completion as identical.

The user object carries tags and custom fields, so the organizational structure you maintain in Grow (region, crew, employee ID) travels with the completion event — you usually don’t need a second lookup to attribute it.


5. Verifying authenticity

Because a webhook endpoint is a URL on the public internet, you must confirm each delivery genuinely came from Grow before trusting it. Grow signs every payload.

Each delivery includes a signature header:

Pl-Webhook-Signature: v1=<hex-digest>

To verify:

  1. Take the raw request body exactly as received (do not re-serialize the JSON first — re-ordering or re-spacing will change the digest).
  2. Compute an HMAC over that raw body using your <WEBHOOK_SECRET> as the key.
  3. Compare your computed digest to the v1=-prefixed value in the Pl-Webhook-Signature header, using a constant-time comparison.
  4. If they match, the payload is authentic and unaltered. If they don’t, reject the request (respond 4xx) and do not process it.

Two operational rules:

  • Always serve <YOUR_ENDPOINT> over HTTPS. Signature verification protects integrity; HTTPS protects the payload (and the signature itself) in transit, closing off man-in-the-middle tampering.
  • Keep <WEBHOOK_SECRET> server-side only. It never belongs in client code, logs, or source control. Plenum provisions it for your environment and can rotate it with you.

6. Operating the endpoint

A few practices keep a webhook integration dependable in production:

  • Be idempotent. Process each event in a way that’s safe to receive more than once. If your receiver is briefly slow or returns a non-2xx, a delivery may be retried — design so a duplicate courseCompleted doesn’t double-count.
  • Acknowledge fast, process async. Return 2xx as soon as you’ve validated the signature and durably queued the payload; do the heavier downstream work outside the request cycle.
  • Reconcile, don’t depend solely on push. Webhooks are the real-time path, not the system of record. If your receiver is down during a delivery, pair webhooks with the event-log pull described in Pull-Based Data Access: replay everything in the gap window and catch up with certainty. Push keeps you current; the event log guarantees you never permanently miss anything.

Subscribing the events above to <YOUR_ENDPOINT>, and configuring <WEBHOOK_SECRET>, is a one-time setup Plenum performs for your environment during onboarding — there’s nothing for your team to operate on the Grow side day-to-day.


7. Where to go next

  • Pull-Based Data Access — the queryable record catalog and the event-log reconciliation pattern referenced above.
  • Authentication & Access Setup — the credentials and token your pull/reconciliation calls use.
  • Data Integration Overview — the high-level push/pull capability map.

Prepared by Plenum Solutions for your evaluation. “Grow by Plenum” is operated and supported by Plenum on your behalf. For integration design support, contact your Plenum engagement lead.