Webhook event list
Your webhook endpoint receives the following JSON bodies through HTTP POST and acknowledges delivery with HTTP 2xx. See Webhook integration for configuration, headers, signatures and retries.
The following ten events are supported. IDs, names, URLs, times and error messages illustrate the payload format. Optional fields may be absent; use the corresponding query API for complete resource details.
Event index
| Event | Meaning | State in the example |
|---|---|---|
classroom.created | Classroom created | pending |
classroom.updated | Classroom updated | pending |
classroom.started | Classroom started | live |
classroom.ended | Classroom ended | ended |
classroom.expired | Classroom expired | expired |
classroom.stage_changed | Member stage occupancy changed | live |
courseware.created | Courseware created | queued |
courseware.processing | Courseware processing | processing |
courseware.ready | Courseware ready | ready |
courseware.failed | Courseware failed | failed |
Envelope and classroom fields
| Field | Type | Required | Meaning |
|---|---|---|---|
eventId | string | Yes | Unique event ID; stable across retries |
type | string | Yes | Full event name |
appId | string | Yes | Owning application |
timestamp | string | Yes | Event creation time in ISO 8601 |
data | object | Yes | Event-specific data |
Classroom events share these data fields:
| Field | Type | Required | Meaning |
|---|---|---|---|
classroomId | string | Yes | Classroom ID |
name | string | Yes | Classroom name at the event |
status | string | Yes | State after the change: creating, pending, live, ended or expired |
teacherEduUserId | string | No | Teacher ID when assigned |
Classroom created
classroom.created
Sent after classroom creation completes and its state is saved. Use it to associate your course with the classroom; it does not indicate client entry.
| Additional data field | Type | Required | Meaning |
|---|---|---|---|
fromStatus | string | Yes | Previous creation state, currently creating. |
Immediate start also produces a separate classroom.started event; this event can already have status: live.
{
"eventId": "evt_example_classroom_created",
"type": "classroom.created",
"appId": "app_example",
"timestamp": "2026-10-09T02:00:00.000Z",
"data": {
"classroomId": "cls_example",
"name": "Math class",
"status": "pending",
"teacherEduUserId": "eu_teacher",
"fromStatus": "creating"
}
}
Classroom updated
classroom.updated
Sent after a classroom name or configuration update. Use it to refresh your stored classroom details.
| Additional data field | Type | Required | Meaning |
|---|---|---|---|
teacherChanged | boolean | Yes | Whether this update changed the teacher. |
The notification contains the common classroom fields and teacherChanged, not a full configuration snapshot. Query the classroom API for details.
{
"eventId": "evt_example_classroom_updated",
"type": "classroom.updated",
"appId": "app_example",
"timestamp": "2026-10-09T02:00:00.000Z",
"data": {
"classroomId": "cls_example",
"name": "Math class",
"status": "pending",
"teacherEduUserId": "eu_teacher",
"teacherChanged": false
}
}
Classroom started
classroom.started
Sent when a classroom enters live. Use it to update course state or start in-class workflows.
| Additional data field | Type | Required | Meaning |
|---|---|---|---|
fromStatus | string | Yes | Classroom state before starting. |
source | string | Yes | Start source; values below. |
startsAt / endsAt | string | No | Scheduled start/end, ISO 8601. |
actualStartedAt | string | No | Actual start, ISO 8601. |
effectiveEndsAt | string | No | Effective end based on actual start, duration and overtime, ISO 8601. |
source: create_immediate (start during creation), manual, auto_reconcile (state reconciliation), auto_job (scheduled job), or lazy_refresh (refresh during a query). Time fields are included when available. This event does not establish client IM, RTC or whiteboard readiness.
{
"eventId": "evt_example_classroom_started",
"type": "classroom.started",
"appId": "app_example",
"timestamp": "2026-10-09T02:00:00.000Z",
"data": {
"classroomId": "cls_example",
"name": "Math class",
"status": "live",
"teacherEduUserId": "eu_teacher",
"fromStatus": "pending",
"source": "manual",
"startsAt": "2026-10-09T02:00:00.000Z",
"endsAt": "2026-10-09T03:00:00.000Z",
"actualStartedAt": "2026-10-09T02:00:00.000Z",
"effectiveEndsAt": "2026-10-09T03:00:00.000Z"
}
}
Classroom ended
classroom.ended
Sent after a live classroom ends manually or automatically. Use it to close the in-class state and begin archival work.
| Additional data field | Type | Required | Meaning |
|---|---|---|---|
fromStatus | string | No | Previous state; batch-ending paths may omit it. |
The event does not contain attendance totals, recording URLs or an actual-end-time field. Delivery time is not a course statistics result.
{
"eventId": "evt_example_classroom_ended",
"type": "classroom.ended",
"appId": "app_example",
"timestamp": "2026-10-09T02:00:00.000Z",
"data": {
"classroomId": "cls_example",
"name": "Math class",
"status": "ended",
"teacherEduUserId": "eu_teacher",
"fromStatus": "live"
}
}
Classroom expired
classroom.expired
Sent when an unstarted classroom meets the server expiration condition and becomes expired. Use it to mark the scheduled classroom as expired.
| Additional data field | Type | Required | Meaning |
|---|---|---|---|
fromStatus | string | No | Previous state; batch-expiration paths may omit it. |
This differs from classroom.ended after a class has started. It does not include an expiration-reason enum or a rescheduling result.
{
"eventId": "evt_example_classroom_expired",
"type": "classroom.expired",
"appId": "app_example",
"timestamp": "2026-10-09T02:00:00.000Z",
"data": {
"classroomId": "cls_example",
"name": "Math class",
"status": "expired",
"teacherEduUserId": "eu_teacher",
"fromStatus": "pending"
}
}
Member stage occupancy changed
classroom.stage_changed
Sent when a member moves between occupied (on) and unoccupied stage states. Use it to synchronize stage membership or record stage activity.
| Additional data field | Type | Required | Meaning |
|---|---|---|---|
action | string | Yes | on: take a seat; off: leave the stage. |
eduUserId | string | Yes | ID of the affected member. |
role | string | Yes | Member role in this classroom. |
fromStageStatus / stageStatus | string | Yes | Previous/new state: off, hand_raised, on. |
actorEduUserId | string | No | Actor ID when available. |
source | string | Yes | Source of the change; values below. |
onStageCount | number | Yes | Stage occupant count after the change, integer. |
source: invite, accept_hand, join, fill_on_start, backfill, leave_stage, leave_classroom, roster_removed, trtc_exit, im_offline, im_timeout. Raising, cancelling or rejecting a hand does not emit it. join refers to entry that changes occupancy, not every classroom entry.
{
"eventId": "evt_example_classroom_stage_changed",
"type": "classroom.stage_changed",
"appId": "app_example",
"timestamp": "2026-10-09T02:00:00.000Z",
"data": {
"classroomId": "cls_example",
"name": "Math class",
"status": "live",
"teacherEduUserId": "eu_teacher",
"action": "on",
"eduUserId": "eu_student",
"role": "student",
"fromStageStatus": "hand_raised",
"stageStatus": "on",
"actorEduUserId": "eu_teacher",
"source": "accept_hand",
"onStageCount": 2
}
}
Courseware fields
All four courseware events share these data fields. Each event's additional fields are listed below its heading.
| Field | Type | Required | Meaning |
|---|---|---|---|
coursewareId | string | Yes | Courseware ID |
name | string | Yes | Courseware name |
format | string | Yes | File format, for example pdf |
status | string | Yes | queued, processing, ready or failed |
progress | number | Yes | Conversion progress, 0–100 |
ownerEduUserId | string | No | Owner ID when recorded |
jobId | string | No | Conversion job ID when recorded |
errorCode / errorMessage | string | No | Recorded error code/message |
resultManifestUrl | string | No | Conversion manifest URL when available |
coursewareBaseUrl | string | No | Converted-resource base URL when available |
Only the creation event adds a classroom binding ID. Do not require it on later conversion events.
Courseware created
courseware.created
Sent after the courseware record is created. Use it to associate an upload task with a courseware ID.
| Additional data field | Type | Required | Meaning |
|---|---|---|---|
fromStatus | string | Yes | Always queued. |
classroomId | string | No | Classroom requested for binding during creation. |
This event uses a creation snapshot with status: queued and progress: 0. An immediate conversion failure produces this event and a separate failure event.
{
"eventId": "evt_example_courseware_created",
"type": "courseware.created",
"appId": "app_example",
"timestamp": "2026-10-09T02:00:00.000Z",
"data": {
"coursewareId": "cw_example",
"name": "lesson.pdf",
"format": "pdf",
"status": "queued",
"ownerEduUserId": "eu_teacher",
"progress": 0,
"fromStatus": "queued",
"classroomId": "cls_example"
}
}
Courseware processing
courseware.processing
Sent when creation, retry or a conversion update moves courseware into processing. Use it to display conversion in progress.
| Additional data field | Type | Required | Meaning |
|---|---|---|---|
fromStatus | string | Yes | Previous state; can be failed for a retry. |
Progress changes while already processing do not emit an event each time. A retry does not emit another courseware.created.
{
"eventId": "evt_example_courseware_processing",
"type": "courseware.processing",
"appId": "app_example",
"timestamp": "2026-10-09T02:00:00.000Z",
"data": {
"coursewareId": "cw_example",
"name": "lesson.pdf",
"format": "pdf",
"status": "processing",
"ownerEduUserId": "eu_teacher",
"progress": 0,
"jobId": "job_example",
"fromStatus": "queued"
}
}
Courseware ready
courseware.ready
Sent when conversion makes courseware ready. Use it to refresh availability and resource locations.
| Additional data field | Type | Required | Meaning |
|---|---|---|---|
fromStatus | string | Yes | State before becoming ready. |
Resource URLs come from conversion results and are included when available. Query classroom bindings separately; classroomId is not guaranteed. Respect resource access controls and expiration.
{
"eventId": "evt_example_courseware_ready",
"type": "courseware.ready",
"appId": "app_example",
"timestamp": "2026-10-09T02:00:00.000Z",
"data": {
"coursewareId": "cw_example",
"name": "lesson.pdf",
"format": "pdf",
"status": "ready",
"ownerEduUserId": "eu_teacher",
"progress": 100,
"jobId": "job_example",
"fromStatus": "processing",
"resultManifestUrl": "https://assets.example.com/courseware/manifest.json",
"coursewareBaseUrl": "https://assets.example.com/courseware/"
}
}
Courseware failed
courseware.failed
Sent when starting conversion fails or a conversion result makes courseware failed. Use it to display the failure and decide whether to retry.
| Additional data field | Type | Required | Meaning |
|---|---|---|---|
fromStatus | string | Yes | Previous state, such as queued or processing. |
errorCode and errorMessage are included only when recorded; do not match the illustrative message in business logic. A start failure may have no jobId.
{
"eventId": "evt_example_courseware_failed",
"type": "courseware.failed",
"appId": "app_example",
"timestamp": "2026-10-09T02:00:00.000Z",
"data": {
"coursewareId": "cw_example",
"name": "lesson.pdf",
"format": "pdf",
"status": "failed",
"ownerEduUserId": "eu_teacher",
"progress": 0,
"fromStatus": "queued",
"errorMessage": "Conversion could not be started."
}
}
Reserved events
The following entries are reserved in the subscription catalog and do not send notifications.
| Catalog entry | Intended meaning indicated by the name | Current delivery |
|---|---|---|
edu_user.registered | User registration | Not emitted |
classroom.member_added | Member added to roster | Not emitted |
classroom.joined | User joined a classroom | Not emitted |
classroom.permission_granted | Classroom permission granted | Not emitted |
classroom.permission_revoked | Classroom permission revoked | Not emitted |
classroom.feature_status_changed | Classroom feature status changed | Not emitted |
media.im_callback, media.trtc_callback and media.live_callback are internal media events, excluded from customer subscriptions. Client callbacks are documented in Client APIs and events.
Webhook user IDs, classroom names and resource URLs must be included in the receiver's data handling inventory. See Data collection.