Webhook integration
Your backend can receive classroom and courseware state changes through webhooks. Configure an HTTPS URL and event subscriptions for an application in the console. When an event occurs, the classroom service sends an HTTP POST containing event data to that URL. Client callbacks such as onClassInfoChanged execute inside the client SDK; server webhooks are received by your backend.
Swipe horizontally to see the full diagram
Configure the console
- Deploy an HTTPS POST endpoint reachable by the service, such as
https://api.example.com/webhooks/eduskit. Accept JSON and preserve the raw request body. Use a valid TLS certificate. - Sign in to the console, open Callbacks, and select the application.
- Enter the callback URL, select at least one event, such as
classroom.started,classroom.endedorcourseware.ready, and save. - Copy the signing secret shown after saving and store it in your receiving service's secret configuration. Webhooks use
webhookSecret, separately from the Server SDK'sappSecret. - Create or start a classroom, or complete a courseware conversion in that application. Check the receiver's logs and persisted records. Saving configuration alone does not produce those business events.
Configuration is per application: one callback URL and one subscription set. The URL limit is 2048 characters. Current console/backend validation accepts HTTP and HTTPS; use HTTPS for production receivers.
The first configuration, a URL change, or Regenerate generates a new signing secret. Its plaintext is returned only at that time. Updating subscriptions while keeping the same URL preserves the secret. Update your receiver after changing the URL or regenerating the secret; the current configuration retains one valid secret. Clearing the URL and saving disables callbacks and removes the secret.
Some events in the subscription catalog do not yet produce notifications. See Webhook event list for actual event availability and fields.
Request format
| Item | Format |
|---|---|
| Method | POST to the configured URL |
| Content-Type | application/json |
x-eduskit-event-id | Same as body eventId |
x-eduskit-event-type | Same as body type |
x-eduskit-timestamp | Unix seconds used to sign this delivery attempt |
x-eduskit-signature | v1= followed by a 64-character hexadecimal HMAC-SHA256 digest |
Example body; IDs and times are illustrative:
{
"eventId": "evt_example_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"
}
}
The body's timestamp is the event creation time in ISO 8601. The header timestamp is generated when sending the attempt and is used for the verification time window. Retries retain the same eventId and event content but generate a fresh signing timestamp. The body does not include internal trace fields.
Receive an event
- Read the raw body bytes and signature headers. Verify with the application's
webhookSecret. Do not parse and reserialize JSON before verification. - After verification, parse JSON, check that
appIdbelongs to the receiving endpoint, and confirm that bodyeventIdandtypematch the headers. - Use a database unique constraint on
(appId, eventId). Persist the received event and pending work in the same transaction. A duplicate event already persisted can be acknowledged immediately. - Return
200or204after persistence succeeds. Execute slow work in a background job. If persistence fails, return non-2xx so delivery can be retried.
See Webhook signature verification for the algorithm and Node.js, Python, Java, Go and PHP examples. These functions verify the signature and time window. Your application supplies the HTTP route, application checks, persistence and idempotency.
Event types and fields may expand. Preserve unknown fields as needed. A verified event that your business logic does not handle can be recorded and acknowledged, avoiding repeated deliveries for an unimplemented branch.
Acknowledgment, timeout and retries
| Condition | Delivery behavior |
|---|---|
| HTTP 200–299 | Success; no specific response body is required |
| Non-2xx, network error or timeout | Retry within the current dispatch |
| One HTTP request | 10-second timeout |
| One dispatch | Up to 3 attempts including the first; delays are 200 ms and 400 ms |
| All attempts fail | The message is requeued and may be delivered again; 3 is not the event's total attempt limit |
| No URL or event not subscribed | Skip delivery to the receiver |
Events can arrive more than once, late or out of order. Use persistent records for deduplication. An HTTP acknowledgment confirms receipt; your system tracks background work separately. Query the Classroom API for current state to avoid overwriting it with an older event. Replay of events skipped while a URL or subscription was disabled is not guaranteed.
Troubleshooting
| Symptom | Check |
|---|---|
| No notification | Correct application, saved URL, selected event, event actually produced, reachable HTTPS endpoint |
| Signature mismatch | Wrong appSecret, old signing secret, body changed by JSON middleware or a gateway |
| Time-window rejection | Receiver clock synchronization; use the header timestamp rather than event creation time |
| Duplicate delivery | Previous timeout/non-2xx; deduplication and business persistence in the same transaction |
| Started notification before client readiness | Server state changes and client IM/RTC/whiteboard readiness are separate stages |
Test valid signatures, modified bodies, expired timestamps, duplicate events, timeouts and HTTP 500 responses. Record appId, eventId, type, receive time and response status for delivery troubleshooting.
Data handling
Include received user IDs, classroom/courseware names and resource URLs in your data inventory. Limit access to stored events, redact logs and define retention for payloads and deduplication keys. See Data collection and Privacy and compliance integration.