Manager Notifications
Webhooks
When a learner enrolls, completes a course, buys something, or hits another trigger you pick, Thought Industries POSTs that event as JSON to a URL you own — your server, a Zapier Catch Hook, or another automation tool. You configure the URL in Admin, not through the REST API.
Choose a channel
Outbound learning data is configured once, in Users → Manager Notifications. Webhook is the real-time channel. The same screen can also email a person or drop a nightly file.
| Channel | Use when | Timing |
|---|---|---|
| Webhook | A CRM, marketing tool, or your own service should react immediately. | As the action happens (see exceptions below). JSON POST to your URL. |
| SFTP | A warehouse or ETL job should pick up a file once a day. | ~2am in the instance timezone, previous day’s events. CSV or JSON. |
| A person on your team should read the event. | Near real-time. Only some triggers offer email. | |
| REST poll | You cannot receive inbound HTTP, or you need to backfill. | You call GET /incoming/v2/events/:kind on your schedule. |
Most webhooks fire as the learner acts. Two exceptions: subscription purchases (bundlePurchase) wait about an hour for financial systems; inactivity webhooks send daily at noon. Course-view reports are SFTP only.
Common jobs
Start with one trigger. You can add more notifications later — each one is a trigger + a channel + a destination.
Push a completion into a CRM or drip a follow-up email
Fires when a learner finishes a course. Typical first webhook.
Create or update a contact when someone enrolls
Fires on enrollment. courseAction covers enroll plus status, certificate, and revoke in one feed.
Issue a badge or update a credential record
Fires when a course certificate should be issued.
Sync a paid purchase to billing or fulfillment
Fires when a course is bought. Subscriptions use bundlePurchase and lag about an hour.
Set it up
You need a public URL that accepts POST. That can be your own HTTPS endpoint, or a no-code catch URL such as a Zapier Catch Hook. Then, in Admin:
- Go to Users → Manager Notifications (
/learn/manager/notification). - Click Add Notification.
- Choose the trigger from the “When a …” list (for example, “course is completed by a learner”).
- Select Webhook. Almost every trigger offers it; course-view reports do not.
- Paste the destination URL. The form example is
https://www.example.org/path. - Optionally limit the notification to one course or learning path. The default is every item on the site.
- Click Test Event. When the sample arrives, click Save Notification.
Screenshots of this path are in Configuring Webhook Data Transfers and Setting Up Automated Data Notifications. Learner Notifications and System Notifications on the same Users menu are different features (campaigns and default learner emails).
Get a sample before you write mapping code
Field sets differ by trigger. Do not invent an envelope. On the same Add Notification form:
- Download Sample JSON (or CSV) for the selected trigger. If the sample is empty, perform that action in the site once — it can take up to two hours to appear.
- Test Event POSTs a representative sample to the URL you entered. Use that to confirm headers, body shape, and that your endpoint is reachable.
The posted body is the same event record as GET /incoming/v2/events/:kind. Realtime delivery wraps the object in a JSON array. Learner email is user; profile fields (name, externalCustomerId, custom refs) are on userDetail; the course or path UUID is notifiableId. IDs are UUIDs.
[
{
"event": "courseCompletion",
"timestamp": "2026-03-13T16:45:00.000Z",
"companyId": "11111111-1111-1111-1111-111111111111",
"notifiableId": "22222222-2222-2222-2222-222222222222",
"title": "API Fundamentals",
"user": "[email protected]",
"userDetail": {
"id": "33333333-3333-3333-3333-333333333333",
"firstName": "Jane",
"lastName": "Doe",
"externalCustomerId": "cust-100"
}
}
]Extra keys from the underlying course-action row are present on a live event. Trust Test Event / Download Sample JSON over this sketch.
Receive the POST
Each firing is its own request. Return a status in 200–308 within 15 seconds or the attempt fails. Failed deliveries retry 10 times with backoff from 20 seconds up to 3600 seconds (about 3.5 hours). There is no delivery log in Admin — watch your own endpoint.
// Acknowledge first — the worker times out at 15 seconds.
app.post('/webhooks/ti', (req, res) => {
res.status(200).end();
const items = Array.isArray(req.body) ? req.body : [req.body];
for (const item of items) {
// item.event is the trigger kind, e.g. "courseCompletion"
// item.user is the learner email
// item.notifiableId is the course (or path) UUID
console.log(item.event, item.user, item.notifiableId);
}
});Acknowledge, then do the work
Return 200–308 immediately. Fan the payload into a queue if you call a CRM or send mail.
Treat retries as duplicates
The same event can arrive more than once. Deduplicate on a stable key (id when present, or event + user + notifiableId + timestamp).
Accept an array or a single object
Realtime delivery encodes the mapped event as a JSON array. A test payload should not break the handler if it arrives as one object.
Logins and visits
Both webhooks include userDetail.role, so you can segment managers from learners in an automation.
| Who | role value |
|---|---|
| Manager | Default Manager or Custom Manager |
| Learner | Guest, Registrant, Comp, or Student — depending on how they got access |
| Dual-role user | Whichever role they are using for that session |
User Logs In fires for managers and learners. User Visits Site fires only when a learner visits.
All webhook triggers
The label is what you pick in Admin. The kind is the event field on the JSON, and the path segment on GET /incoming/v2/events/:kind.
Course
courseEnrollmentLearner enrolls in a course
courseActionLearner enrolls, changes status, earns a certificate, or has access revoked in a course
courseCompletionCourse is completed by a learner
courseCertificateCourse certificate should be issued
coursePurchaseCourse is purchased
quizAttemptQuiz is taken (includes tests, surveys, and reflections)
assignmentSubmissionAssignment is submitted by a learner
courseCommentComment is created
courseInstructorMessageInstructor is messaged
emailCaptureEmail is entered on a course paywall or waitlist
Learning path
learningPathEnrollmentLearner enrolls in a learning path
learningPathActionLearner enrolls, changes status, earns a certificate, or has access revoked in a learning path
Learner
loginLearner logs in
Also fires for manager logins. Use userDetail.role to tell them apart.
visitLogged-in learner visits the school
Learners only — manager visits do not fire this webhook.
registrationLearner registers as part of free registration
onboardingSurveyResponseOnboarding survey is submitted
awardAward or credit is granted
redemptionCodeRedeemRedemption or registration code is redeemed
userLastActive7Learner is inactive for 7 consecutive days
Batch. Webhooks send daily at 12:00 in the instance timezone.
userLastActive14Learner is inactive for 14 consecutive days
Batch. Webhooks send daily at 12:00 in the instance timezone.
userLastActive31Learner is inactive for 31 consecutive days
Batch. Webhooks send daily at 12:00 in the instance timezone.
Commerce
productPurchaseProduct is purchased
bundlePurchaseSubscription is purchased or auto-renewed
About a one-hour delay while the purchase clears financial systems.
discountGroupPurchaseCourse collection is purchased
pickableGroupPurchaseA la carte collection is purchased
subscriptionCancelSubscription is canceled
subscriptionReactivationSubscription is reactivated
newsletterSignupNewsletter is signed up during checkout
SCORM, meetings, and discussions
scormAttemptSCORM attempt record is updated
meetingAttendanceUpdateMeeting attendance is updated
discussionsWidgetCommentComment is created in a discussion widget
SFTP and polling
Nightly SFTP
Same Manager Notification screen, SFTP channel. File of the previous day’s events around 2am, CSV or JSON. 60s connect timeout, 5 minutes max transfer, same 10-retry backoff. Use this for a warehouse, not a live CRM write.
Poll the Events API
Call GET /incoming/v2/events/:kind with a startDate in epoch milliseconds and follow pageInfo.cursor. See Webhooks vs REST and Course Actions.
Real-time first
Webhook when another system should react as the learner acts.
Email a person
Same screen, Email channel — only on triggers that have a template.
We retry for you
10 attempts, 20s to 3600s. Still return 200–308 within 15 seconds.
Admin screenshots: Setting Up Automated Data Notifications. Webhook-specific notes: Configuring Webhook Data Transfers.