Skip to content

Webinars

Needs review

Needs review: Selling a webinar: the product block on webinar resources is only filled when YouTube is configured and the webinar has a live stream (App\Providers\ShopServiceProvider). Check whether a webinar without YouTube can be bought end to end.

A webinar is a scheduled live session with one or more trainers. Learners who have access (usually by buying the webinar’s product) join a video room at the scheduled time. The room is a Jitsi room, either on a self-hosted Jitsi server or on 8x8 JaaS. Optionally the session is broadcast to YouTube as an unlisted live stream.

Three packages are involved:

Package Role
webinar Webinar model, admin and learner endpoints, reminders, trainer assignment events
jitsi Builds the room parameters and the signed JWT for Jitsi or JaaS; receives the recording webhook
pencil-spaces Pencil Spaces client. Not used by webinars; it creates rooms for approved consultation requests
  • Administrators create webinars, assign trainers and manage the product. The admin panel menu entry needs webinar_list; the edit screen needs webinar_read.
  • Tutors (trainers) are moderators in the room. With the default roles they hold only the -own permissions (see Permissions), so the admin menu entry is hidden for them.
  • Learners see published webinars on the learner site and join from their account once the session has started.

/courses/webinars/list (Courses → Webinars). Columns: ID, name, status, product, duration, active from, active to and tags. Filters: date range, name and tags. When loading the list fails with a YouTube error, the panel opens a Generate token dialog: it asks for an e-mail address, calls POST /api/admin/g-token/generate and opens the Google consent page that reconnects the YouTube account used for live streams.

Webinar list in the admin panel

The entry can be hidden with the dashboard setting hideInMenu-CoursesWebinars.

/courses/webinars/webinar/:webinar (new to create). Tabs:

Tab Content
Attributes Name, duration (free text such as 2 hours), status (draft, published, archived), active from, active to, trainers, the “AI Recording Analysis” switch, short description, program (agenda) and description
Product Price and sale settings of the webinar’s product (see Sales)
Media Cover image
Tags Tags used for filtering
Branding Logotype; shown inside the Jitsi room instead of the default logo
User submission Assign users by e-mail, including people without an account yet (assign-without-account)

The tabs other than Attributes appear after the first save.

Webinar edit form with the Attributes tab open
  1. A learner gets access, normally by buying the webinar product; the webinar is then attached to the user (webinar_user). Trainers are attached through the form.
  2. GET /api/webinars/generate-jitsi/{id} returns the room data. It answers 404 unless the webinar is published and the current time is between active_to and active_to + duration. When YouTube is configured, the webinar must also have a YouTube stream.
  3. Trainers get moderator rights in the room; the room name is derived from the webinar name.
  4. The response also carries yt_url, yt_stream_url and yt_stream_key, which the trainer uses to broadcast. Trainers start and stop the YouTube broadcast with GET /api/webinars/start-live-stream/{id} and GET /api/webinars/stop-live-stream/{id}.

The jitsi package picks the mode from the configuration:

  • JaaS when app_id, jaas_host, aud, iss, kid and private_key are all set.
  • Self-hosted Jitsi when jitsi_host, app_id and secret are set. The token is signed with HS256; the secret must be at least 32 bytes.
  • Otherwise no JWT is generated and the room data contains only the domain and room name.
Config key (jitsi.*) Environment variable Default
jitsi_host JITSI_HOST meet-stage.ulams.app
app_id JITSI_APP_ID meet-id
secret JITSI_APP_SECRET Test
jaas_host JAAS_HOST https://8x8.vc/
aud, iss, sub JAAS_AUD, JAAS_ISS, JAAS_SUB jitsi, chat, empty
kid, private_key JAAS_KEY_ID, JAAS_PRIVATE_KEY empty
recording JAAS_RECORDING false
package_status none enabled; any other value makes room generation return “Package is disabled”

All keys above are also registered as administrable settings, so they can be changed per tenant in Settings under the jitsi group.

POST /api/jitsi/recorded-video receives finished recordings, downloads the file and stores it under jitsi/videos/... on the tenant’s storage disk. Requests are rejected unless one of these is set:

  • JITSI_WEBHOOK_SECRET: JaaS signing secret, checked against the X-Jaas-Signature header (tolerance JITSI_WEBHOOK_TOLERANCE, default 300 seconds);
  • JITSI_WEBHOOK_TOKEN: shared token for self-hosted Jitsi, sent as Authorization: Bearer <token>.

Downloads are allowed only over HTTPS from hosts in JITSI_RECORDING_HOSTS (comma-separated, *.example.com matches subdomains), never from private addresses, only mp4/webm, up to JITSI_RECORDING_MAX_BYTES (default 2 GB) and JITSI_RECORDING_DOWNLOAD_TIMEOUT (default 600 s). The endpoint is throttled to 60 requests per minute.

For JaaS, set JITSI_WEBHOOK_SECRET to the endpoint secret shown by the “Reveal secret” button in the JaaS console (Webhooks). JaaS signs <timestamp>.<raw body> with HMAC-SHA256, base64 encoded, in X-Jaas-Signature: t=<timestamp>,v1=<signature>; schemes other than v1 are ignored. The recording link of RECORDING_UPLOADED is a pre-authenticated, 24-hour URL on Oracle object storage (https://objectstorage.<region>.oraclecloud.com/...mp4), so JaaS deployments set JITSI_RECORDING_HOSTS=*.oraclecloud.com, or the exact region host if it is known. Both formats follow the JaaS webhook documentation (checked 2026-10-09). Self-hosted Jitsi leaves the secret empty and uses JITSI_WEBHOOK_TOKEN with its own recording host.

Live streaming uses the youtube package (Google OAuth client ID, secret and API key, plus a refresh token obtained through the Google consent flow). When YouTube is configured, saving a webinar creates or updates an unlisted YouTube broadcast. If it is not configured, webinars run in the video room only.

Method and path Purpose
GET/POST /api/admin/webinars, GET/PUT/DELETE /api/admin/webinars/{id}, POST /api/admin/webinars/{id} Admin CRUD (the POST variant accepts multipart uploads)
GET /api/admin/webinars/{id}/users Users attached to a webinar
GET /api/admin/webinars/users/assignable Users that can be assigned as trainers
GET /api/webinars, GET /api/webinars/{id} Public list and detail
GET /api/webinars/me Webinars of the current user
GET /api/webinars/generate-jitsi/{id} Room data for the current user
GET /api/webinars/start-live-stream/{id}, GET /api/webinars/stop-live-stream/{id} Control the YouTube broadcast
POST /api/pencil-spaces/login Direct login link to Pencil Spaces (needs pencil-spaces_login)

Full list: API endpoints.

Permission admin tutor
webinar_list, webinar_read, webinar_create, webinar_update, webinar_delete yes webinar_create only
webinar_list-own, webinar_read-own, webinar_update-own, webinar_delete-own no yes

The -own variants apply to webinars where the user is a trainer. The pencil-spaces_login permission exists, but the application’s permission seeder does not run the Pencil Spaces seeder, so no role has it by default. See Permissions.

Event When E-mail template
WebinarTrainerAssigned A trainer is added to a webinar yes
WebinarTrainerUnassigned A trainer is removed yes
ReminderAboutTerm One day and one hour before the start, for each attached user yes
YtProblem (youtube package) The YouTube API call fails yes

Reminders come from the scheduled job ReminderAboutWebinarJob: the one-hour reminder runs every five minutes, the one-day reminder every six hours. Each webinar stores the last reminder sent so a user is not reminded twice. See Scheduled jobs, Templates and Events and notifications.

  • The admin panel only works for users with webinar_list; tutors cannot manage their own webinars there with the default roles.
  • The learner site (front/web) lists webinars read-only; buying and joining happen in the legacy learner app or a custom front. See Learners.
  • The JaaS webhook signature format still has to be confirmed against the JaaS documentation (open roadmap item).