Thrive can notify your systems in real-time when key events occur on the platform. When a matching event fires, Thrive sends an HTTP POST request containing a JSON payload to the URL you have configured for that subscription type.
Implementation will handle the configuration setup alongside you.
Subscription Types
Webhooks are grouped into six subscription types. Each subscription type has its own URL configuration and delivers a distinct set of events.
| Subscription | Events Delivered |
|---|---|
completion_subscription |
content.completed, content.passed |
content_subscription |
content.*, page.*, post.*, comment.*, moderation.* |
assignment_subscription |
assignment.*, enrolment.* |
notification_subscription |
notification.dispatched |
user_subscription |
user.activated, user.deactivated, user.updated |
event_subscription |
event.* |
Common Payload Fields
Every event payload includes the following top-level fields regardless of event type.
| Field | Type | Description |
|---|---|---|
eventType |
string | Identifies the specific event, e.g. content.completed |
tenantId |
string | Your Thrive tenant identifier |
createdAt |
string | ISO 8601 timestamp of when the event occurred on the platform |
dispatchedAt |
string | ISO 8601 timestamp of when this webhook request was dispatched |
Setting Up Your Webhooks
You configure webhooks yourself by sending a short request to Thrive for each subscription type you want to receive. Each subscription has its own configuration that tells Thrive where to send events (a URL) and, optionally:
- a secret used to sign requests with HMAC so you can verify they came from Thrive (see Verifying the HMAC Signature)
- OAuth 2.0 client credentials so Thrive obtains a bearer token from your identity provider and sends it on each delivery (see OAuth 2.0 Authentication)
HMAC and OAuth are independent — you can use either, both, or neither.
There are six subscription types (see Subscription Types above), so you can set up to configurations. If you want to receive everything at a single endpoint, simply use the same URL in all six.
Step 1 - Get your credentials
Configuration requires administrator access. Follow Step 1 of the Authentication guide to copy two values from your browser while signed in as an admin:
- your
Authorizationheader (the value beginning withBearer) - your
Correlation-Idheader
You'll send these with each configuration request in Step 3.
Step 2 - Choose your environment
Send your configuration request to the endpoint for the environment you're setting up:
| Environment | Endpoint |
|---|---|
| Production (live) | https://tenant.api.learn.link/config |
| Staging | https://tenant.api.learnstaging.link/config |
Requests are sent as an HTTP POST with your credentials from Step 1 in the request headers.
Step 3 - Send a configuration for each subscription
For each subscription type you want to enable, send the following GraphQL mutation. Set key to the subscription type and value to a JSON string containing your url (and optionally secret and/or auth):
HMAC only (optional secret):
mutation {
setSecureConfig(
input: {
category: "webhooks"
key: "content_subscription"
value: "{\"url\":\"https://example.com/webhooks\",\"secret\":\"your-secret\"}"
}
) {
isSet
}
}
OAuth 2.0 client credentials:
mutation {
setSecureConfig(
input: {
category: "webhooks"
key: "content_subscription"
value: "{\"url\":\"https://example.com/webhooks\",\"auth\":{\"type\":\"oauth2\",\"tokenUrl\":\"https://auth.example.com/oauth/token\",\"clientId\":\"your-client-id\",\"clientSecret\":\"your-client-secret\",\"grantType\":\"client_credentials\"}}"
}
) {
isSet
}
}
You can include both secret and auth in the same value if you want HMAC signing and a bearer token on each request.
key- the subscription type. One of:completion_subscription,content_subscription,assignment_subscription,notification_subscription,user_subscription,event_subscription.value- a JSON string (with quotes escaped, as shown) containing:url(required) - the HTTPS endpoint Thrive should send events to. Must be publicly reachable and usehttps://.secret(optional) - a secret used to sign each request. If you provide one, Thrive adds anx-hmac-signatureheader you can verify (see Verifying the HMAC Signature). Omit it if you don't need signature verification:value: "{\"url\":\"https://example.com/webhooks\"}"auth(optional) - OAuth 2.0 settings. When present withtype: "oauth2", Thrive fetches an access token from yourtokenUrland sendsAuthorization: Bearer <access_token>on each delivery (see OAuth 2.0 Authentication).
If you'd rather use the command line, an HMAC example looks like this:
curl https://tenant.api.learn.link/config \
-X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_BEARER_TOKEN" \
-H "Correlation-Id: YOUR_CORRELATION_ID" \
-d '{"query":"mutation { setSecureConfig(input: { category: \"webhooks\", key: \"content_subscription\", value: \"{\\\"url\\\":\\\"https://example.com/webhooks\\\",\\\"secret\\\":\\\"your-secret\\\"}\" }) { isSet } }"}'
A successful response returns:
{ "data": { "setSecureConfig": { "isSet": true } } }
Note:
isSetconfirms your configuration was saved. If you re-send the exact same value a second time,isSetreturnsfalsebecause nothing changed, your configuration is still in place.
Step 4 - Repeat for each subscription
Repeat Step 3 for each subscription type you want to receive, changing the key (and the url if you're using different endpoints). To send every event to one endpoint, use the same url in all six configurations.
To update a webhook later, send the mutation again with the new value. Changes can take up to 15 minutes to take effect.
HTTP Request Details
Every webhook request is sent as:
- Method:
POST - Content-Type:
application/json - Body: JSON-encoded event payload
- Optional headers (depending on config):
x-hmac-signature: sha256=<hex-digest>when asecretis configuredAuthorization: Bearer <access_token>when OAuth 2.0 (auth.type: "oauth2") is configured
Your endpoint must respond with a 2xx status code within 30 seconds. If the request times out or returns a non-2xx response, Thrive will retry delivery.
Verifying the HMAC Signature
If a secret was configured for your webhook, each request will include an x-hmac-signature header:
x-hmac-signature: sha256=<hex-digest>
To verify the request came from Thrive:
- Read the raw request body as a string - do not re-parse and re-serialize the JSON, as this can alter key ordering and invalidate the signature.
- Compute HMAC-SHA256 of the raw body string using your configured secret.
- Compare your computed hex digest against the value after
sha256=in the header.
Example (Node.js):
const crypto = require('crypto');
function verifySignature(rawBody, secret, signatureHeader) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return `sha256=${expected}` === signatureHeader;
}
OAuth 2.0 Authentication
If OAuth 2.0 is configured under auth with type: "oauth2", Thrive obtains an access token from your identity provider using the client credentials grant, then includes it on each webhook POST:
Authorization: Bearer <access_token>
auth fields
| Field | Required | Description |
|---|---|---|
type |
Yes | Must be "oauth2". Other values are ignored (delivery continues without a bearer token). |
tokenUrl |
Yes | HTTPS token endpoint Thrive will call with grant_type=client_credentials. |
clientId |
Yes | OAuth client id issued by your identity provider. |
clientSecret |
Yes | OAuth client secret issued by your identity provider. |
grantType |
No | Defaults to client_credentials. |
scope |
No | Optional scope string sent to the token endpoint when present. |
url and tokenUrl must use HTTPS. Private IPs, localhost, .internal, and .local hosts are not allowed.
Example config value (pretty-printed)
{
"url": "https://api.example.com/learning/v1/events",
"auth": {
"type": "oauth2",
"tokenUrl": "https://api.example.com/identity/v4/issue-token/token",
"clientId": "your-client-id",
"clientSecret": "your-client-secret",
"grantType": "client_credentials",
"scope": "optional-scope"
}
}
Behaviour
- Thrive caches tokens briefly and reuses them across deliveries for the same tenant credentials.
- If your webhook endpoint responds with
401or403, Thrive refreshes the token once and retries the delivery. - You may combine OAuth with HMAC by also setting top-level
secret— bothAuthorizationandx-hmac-signatureare then sent.
Your endpoint should validate the bearer token according to your identity provider (for example JWT signature and expiry, or introspection). Thrive does not define the token format; it forwards the access_token returned by your tokenUrl.
Delivery Guarantees
| Behaviour | Detail |
|---|---|
| At-least-once delivery | Your endpoint may receive the same event more than once. Design your integration to handle duplicate events idempotently. |
| Retries | Failed deliveries (timeout or non-2xx response) are retried automatically. |
| Config propagation | Changes to your webhook URL or auth config may take up to 15 minutes to take effect. |
| HTTPS only | Webhook url and OAuth tokenUrl values must use HTTPS. |
Events Overview
Content Events
| Webhook Event | When is it Triggered? | Typical Use Case |
|---|---|---|
content.created |
When a new draft is saved within the content creator. | Detect the initial creation of new content. |
content.published |
When a content item transitions from draft to a published/visible state. | Detect newly available training content. |
content.updated |
When a draft or published content item is updated. | Synchronise changes to existing content. |
content.archived |
When a published content item is archived. | Synchronise content no longer being accessible to users. |
content.restored |
When an archived content item is restored to the published state. | Synchronise content becoming accessible again. |
content.deleted |
When an archived content item is deleted. | Synchronise content no longer existing on the platform. |
content.completed |
When a user completes a content item. | Track learner completions for reporting/compliance. |
content.passed |
When a user passes a content item (e.g. achieves a passing score on an assessment). | Track learner pass/fail assessment results. |
Page Events
| Webhook Event | When is it Triggered? | Typical Use Case |
|---|---|---|
page.published |
When a community or content page is published. | Detect newly published pages. |
page.deleted |
When a community or content page is deleted. | Synchronise removal of pages. |
Post Events
| Webhook Event | When is it Triggered? | Typical Use Case |
|---|---|---|
post.posted |
When a post is created within a community page. | Detect new posts. |
post.updated |
When a post is updated. | Synchronise post edits. |
post.deleted |
When a post is deleted. | Synchronise post removals. |
post.liked |
When a post is liked. | Track engagement/likes. |
post.unliked |
When a like is removed from a post. | Track changes in engagement. |
post.pinned |
When a post is pinned. | Detect highlighted/pinned content. |
post.unpinned |
When a post is unpinned. | Detect removal of pinned status. |
post.users-mentioned |
When users are mentioned in a post. | Notify or track user mentions. |
Comment Events
| Webhook Event | When is it Triggered? | Typical Use Case |
|---|---|---|
comment.replied |
When a user replies to a comment on a post. | Track comment reply activity. |
comment.mentioned |
When a user is mentioned in a comment. | Notify or track mentions in comments. |
comment.liked |
When a comment is liked. | Track engagement on comments. |
Moderation Events
| Webhook Event | When is it Triggered? | Typical Use Case |
|---|---|---|
moderation.post-flagged |
When a post is flagged for moderation. | Trigger moderation review workflows for posts. |
moderation.comment-flagged |
When a comment is flagged for moderation. | Trigger moderation review workflows for comments. |
Event Lifecycle Events
| Webhook Event | When is it Triggered? | Typical Use Case |
|---|---|---|
event.created |
When an event draft is created. | Detect the initial creation of new events. |
event.published |
When an event is published for the first time. | Create calendar entries for newly available events. |
event.updated |
When a published event's content changes. | Synchronise changes to existing events. |
event.archived |
When an event is archived. | Remove calendar entries for events that are no longer running. |
event.restored |
When an archived event is restored. | Reinstate events that have become active again. |
event.deleted |
When an event is deleted. | Synchronise events no longer existing on the platform. |
event.occurrence-created |
When an occurrence is added to an event. | Create a calendar entry for a newly scheduled date. |
event.occurrence-updated |
When an occurrence's label, timezone, hosts, capacity or sessions change. | Keep calendar entries in step with schedule changes. |
event.occurrence-deleted |
When an occurrence is removed from an event. | Remove calendar entries for cancelled dates. |
event.attendee-registered |
When one or more users are registered onto an occurrence. | Add attendees to an external calendar or roster. |
event.attendee-cancelled |
When one or more users are removed from an occurrence. | Remove attendees from an external calendar or roster. |
event.attendance-marked |
When attendance is recorded for an occurrence. | Track attendance for reporting/compliance. |
Assignment Events
| Webhook Event | When is it Triggered? | Typical Use Case |
|---|---|---|
assignment.created |
When an assignment is created. | Detect newly created assignments. |
assignment.updated |
When assignment metadata is updated (e.g. due date, primary content, settings). | Keep external systems in sync with assignment changes. |
assignment.archived |
When an assignment is archived/deactivated. | Detect that an assignment is no longer active. |
Enrolment Events
| Webhook Event | When is it Triggered? | Typical Use Case |
|---|---|---|
enrolment.created |
When enrolment records are created after an audience is assigned training. | Detect that users have been enrolled onto training. |
enrolment.users-created |
When users receive enrolments because an assignment is added to an audience, or a user joins an audience with an existing assignment. | Bulk processing of newly enrolled users. |
enrolment.users-archived |
When an assignment is removed from an audience. | Detect which users have been unenrolled. |
enrolment.updated |
When assignment metadata changes result in enrolments being updated. | Track enrolment-level updates. |
enrolment.archived |
When an assignment/enrolment is unassigned or deleted. | Detect enrolments that are no longer active. |
Notification Events
| Webhook Event | When is it Triggered? | Typical Use Case |
|---|---|---|
notification.dispatched |
When a notification is dispatched to a user on the platform (covers assignment, event, social, goal, skill, mentorship, and account notification types). | Relay/sync platform notifications to external systems. |
User Events
| Webhook Event | When is it Triggered? | Typical Use Case |
|---|---|---|
user.created |
When a user account is created on the platform. | Sync new user accounts to external HR/identity systems. |
user.activated |
When a user account is activated on the platform. | Detect account activation. |
user.deactivated |
When a user account is deactivated. | Detect offboarding/deactivation. |
user.updated |
When a user's profile details are updated. | Sync profile changes to external systems. |